From d00115111c6ab0c3f82117c4e2c3d24663c4f1ce Mon Sep 17 00:00:00 2001 From: Creamy Goodness Date: Tue, 19 Oct 2010 22:09:09 -0600 Subject: [PATCH] added dbus support added new variables ${cell_radio_dbm} and ${cell_radio_percent} --- .gitignore | 7 + INSTALL | 133 +- build_notes.txt | 38 + config.guess | 107 +- config.sub | 67 +- configure | 3 +- configure.ac | 1 + configure.ac.in | 1 + debian/README.Debian | 6 + debian/control | 4 +- debian/dirs | 2 + debian/docs | 3 + debian/files | 2 - debian/rules | 16 +- m4/libtool.m4 | 7377 ------------------------ m4/ltoptions.m4 | 368 -- m4/ltsugar.m4 | 123 - m4/ltversion.m4 | 23 - m4/lt~obsolete.m4 | 92 - src/common.h | 3 +- src/conky.c | 8 +- src/conky.h | 6 +- src/core.c | 7 +- src/dbus/.deps/dbus-address.Plo | 1 + src/dbus/.deps/dbus-auth-script.Plo | 1 + src/dbus/.deps/dbus-auth-util.Plo | 1 + src/dbus/.deps/dbus-auth.Plo | 1 + src/dbus/.deps/dbus-bus.Plo | 1 + src/dbus/.deps/dbus-connection.Plo | 1 + src/dbus/.deps/dbus-credentials-util.Plo | 1 + src/dbus/.deps/dbus-credentials.Plo | 1 + src/dbus/.deps/dbus-dataslot.Plo | 1 + src/dbus/.deps/dbus-errors.Plo | 1 + src/dbus/.deps/dbus-hash.Plo | 1 + src/dbus/.deps/dbus-internals.Plo | 1 + src/dbus/.deps/dbus-keyring.Plo | 1 + src/dbus/.deps/dbus-list.Plo | 1 + src/dbus/.deps/dbus-mainloop.Plo | 1 + src/dbus/.deps/dbus-marshal-basic.Plo | 1 + src/dbus/.deps/dbus-marshal-byteswap-util.Plo | 1 + src/dbus/.deps/dbus-marshal-byteswap.Plo | 1 + src/dbus/.deps/dbus-marshal-header.Plo | 1 + src/dbus/.deps/dbus-marshal-recursive-util.Plo | 1 + src/dbus/.deps/dbus-marshal-recursive.Plo | 1 + src/dbus/.deps/dbus-marshal-validate-util.Plo | 1 + src/dbus/.deps/dbus-marshal-validate.Plo | 1 + src/dbus/.deps/dbus-memory.Plo | 1 + src/dbus/.deps/dbus-mempool.Plo | 1 + src/dbus/.deps/dbus-message-factory.Plo | 1 + src/dbus/.deps/dbus-message-util.Plo | 1 + src/dbus/.deps/dbus-message.Plo | 1 + src/dbus/.deps/dbus-misc.Plo | 1 + src/dbus/.deps/dbus-object-tree.Plo | 1 + src/dbus/.deps/dbus-pending-call.Plo | 1 + src/dbus/.deps/dbus-resources.Plo | 1 + src/dbus/.deps/dbus-server-debug-pipe.Plo | 1 + src/dbus/.deps/dbus-server-socket.Plo | 1 + src/dbus/.deps/dbus-server-unix.Plo | 1 + src/dbus/.deps/dbus-server.Plo | 1 + src/dbus/.deps/dbus-sha.Plo | 1 + src/dbus/.deps/dbus-shell.Plo | 1 + src/dbus/.deps/dbus-signature.Plo | 1 + src/dbus/.deps/dbus-spawn.Plo | 1 + src/dbus/.deps/dbus-string-util.Plo | 1 + src/dbus/.deps/dbus-string.Plo | 1 + src/dbus/.deps/dbus-sysdeps-pthread.Plo | 1 + src/dbus/.deps/dbus-sysdeps-unix.Plo | 1 + src/dbus/.deps/dbus-sysdeps-util-unix.Plo | 1 + src/dbus/.deps/dbus-sysdeps-util.Plo | 1 + src/dbus/.deps/dbus-sysdeps.Plo | 1 + src/dbus/.deps/dbus-test.Plo | 1 + src/dbus/.deps/dbus-threads.Plo | 1 + src/dbus/.deps/dbus-timeout.Plo | 1 + src/dbus/.deps/dbus-transport-socket.Plo | 1 + src/dbus/.deps/dbus-transport-unix.Plo | 1 + src/dbus/.deps/dbus-transport.Plo | 1 + src/dbus/.deps/dbus-userdb-util.Plo | 1 + src/dbus/.deps/dbus-userdb.Plo | 1 + src/dbus/.deps/dbus-uuidgen.Plo | 1 + src/dbus/.deps/dbus-watch.Plo | 1 + src/dbus/Makefile | 1026 ++++ src/dbus/Makefile.am | 202 + src/dbus/Makefile.in | 1026 ++++ src/dbus/dbus-address.c | 826 +++ src/dbus/dbus-address.h | 61 + src/dbus/dbus-arch-deps.h | 67 + src/dbus/dbus-arch-deps.h.in | 67 + src/dbus/dbus-auth-script.c | 803 +++ src/dbus/dbus-auth-script.h | 39 + src/dbus/dbus-auth-util.c | 168 + src/dbus/dbus-auth.c | 2690 +++++++++ src/dbus/dbus-auth.h | 81 + src/dbus/dbus-bus.c | 1537 +++++ src/dbus/dbus-bus.h | 82 + src/dbus/dbus-connection-internal.h | 120 + src/dbus/dbus-connection.c | 5901 +++++++++++++++++++ src/dbus/dbus-connection.h | 406 ++ src/dbus/dbus-credentials-util.c | 204 + src/dbus/dbus-credentials.c | 507 ++ src/dbus/dbus-credentials.h | 78 + src/dbus/dbus-dataslot.c | 477 ++ src/dbus/dbus-dataslot.h | 96 + src/dbus/dbus-errors.c | 417 ++ src/dbus/dbus-errors.h | 82 + src/dbus/dbus-hash.c | 2193 +++++++ src/dbus/dbus-hash.h | 142 + src/dbus/dbus-internals.c | 951 +++ src/dbus/dbus-internals.h | 346 ++ src/dbus/dbus-keyring.c | 1154 ++++ src/dbus/dbus-keyring.h | 52 + src/dbus/dbus-list.c | 1405 +++++ src/dbus/dbus-list.h | 98 + src/dbus/dbus-macros.h | 137 + src/dbus/dbus-mainloop.c | 908 +++ src/dbus/dbus-mainloop.h | 76 + src/dbus/dbus-marshal-basic.c | 1988 +++++++ src/dbus/dbus-marshal-basic.h | 261 + src/dbus/dbus-marshal-byteswap-util.c | 105 + src/dbus/dbus-marshal-byteswap.c | 246 + src/dbus/dbus-marshal-byteswap.h | 42 + src/dbus/dbus-marshal-header.c | 1489 +++++ src/dbus/dbus-marshal-header.h | 133 + src/dbus/dbus-marshal-recursive-util.c | 3565 ++++++++++++ src/dbus/dbus-marshal-recursive.c | 2739 +++++++++ src/dbus/dbus-marshal-recursive.h | 196 + src/dbus/dbus-marshal-validate-util.c | 588 ++ src/dbus/dbus-marshal-validate.c | 1204 ++++ src/dbus/dbus-marshal-validate.h | 203 + src/dbus/dbus-memory.c | 842 +++ src/dbus/dbus-memory.h | 59 + src/dbus/dbus-mempool.c | 577 ++ src/dbus/dbus-mempool.h | 44 + src/dbus/dbus-message-factory.c | 1228 ++++ src/dbus/dbus-message-factory.h | 61 + src/dbus/dbus-message-internal.h | 72 + src/dbus/dbus-message-private.h | 125 + src/dbus/dbus-message-util.c | 1320 +++++ src/dbus/dbus-message.c | 4083 +++++++++++++ src/dbus/dbus-message.h | 233 + src/dbus/dbus-misc.c | 248 + src/dbus/dbus-misc.h | 51 + src/dbus/dbus-object-tree.c | 1949 +++++++ src/dbus/dbus-object-tree.h | 62 + src/dbus/dbus-pending-call-internal.h | 67 + src/dbus/dbus-pending-call.c | 826 +++ src/dbus/dbus-pending-call.h | 65 + src/dbus/dbus-protocol.h | 439 ++ src/dbus/dbus-resources.c | 194 + src/dbus/dbus-resources.h | 52 + src/dbus/dbus-server-debug-pipe.c | 432 ++ src/dbus/dbus-server-debug-pipe.h | 47 + src/dbus/dbus-server-protected.h | 160 + src/dbus/dbus-server-socket.c | 539 ++ src/dbus/dbus-server-socket.h | 49 + src/dbus/dbus-server-unix.c | 236 + src/dbus/dbus-server-unix.h | 37 + src/dbus/dbus-server.c | 1202 ++++ src/dbus/dbus-server.h | 91 + src/dbus/dbus-sha.c | 968 ++++ src/dbus/dbus-sha.h | 55 + src/dbus/dbus-shared.h | 131 + src/dbus/dbus-shell.c | 640 ++ src/dbus/dbus-shell.h | 41 + src/dbus/dbus-signature.c | 542 ++ src/dbus/dbus-signature.h | 81 + src/dbus/dbus-spawn.c | 1457 +++++ src/dbus/dbus-spawn.h | 61 + src/dbus/dbus-string-private.h | 126 + src/dbus/dbus-string-util.c | 878 +++ src/dbus/dbus-string.c | 2907 ++++++++++ src/dbus/dbus-string.h | 321 ++ src/dbus/dbus-sysdeps-pthread.c | 324 ++ src/dbus/dbus-sysdeps-unix.c | 3356 +++++++++++ src/dbus/dbus-sysdeps-unix.h | 134 + src/dbus/dbus-sysdeps-util-unix.c | 1237 ++++ src/dbus/dbus-sysdeps-util.c | 176 + src/dbus/dbus-sysdeps.c | 1092 ++++ src/dbus/dbus-sysdeps.h | 505 ++ src/dbus/dbus-test-main.c | 54 + src/dbus/dbus-test.c | 190 + src/dbus/dbus-test.h | 83 + src/dbus/dbus-threads-internal.h | 53 + src/dbus/dbus-threads.c | 816 +++ src/dbus/dbus-threads.h | 196 + src/dbus/dbus-timeout.c | 490 ++ src/dbus/dbus-timeout.h | 75 + src/dbus/dbus-transport-protected.h | 143 + src/dbus/dbus-transport-socket.c | 1339 +++++ src/dbus/dbus-transport-socket.h | 45 + src/dbus/dbus-transport-unix.c | 181 + src/dbus/dbus-transport-unix.h | 37 + src/dbus/dbus-transport.c | 1411 +++++ src/dbus/dbus-transport.h | 92 + src/dbus/dbus-types.h | 139 + src/dbus/dbus-userdb-util.c | 442 ++ src/dbus/dbus-userdb.c | 665 +++ src/dbus/dbus-userdb.h | 121 + src/dbus/dbus-uuidgen.c | 129 + src/dbus/dbus-uuidgen.h | 47 + src/dbus/dbus-watch.c | 668 +++ src/dbus/dbus-watch.h | 82 + src/dbus/dbus.h | 103 + src/defconfig.h | 90 - src/linux.c | 152 +- src/text_object.h | 2 + 205 files changed, 75653 insertions(+), 8252 deletions(-) create mode 100644 build_notes.txt create mode 100644 debian/README.Debian create mode 100644 debian/dirs create mode 100644 debian/docs delete mode 100644 debian/files delete mode 100644 m4/libtool.m4 delete mode 100644 m4/ltoptions.m4 delete mode 100644 m4/ltsugar.m4 delete mode 100644 m4/ltversion.m4 delete mode 100644 m4/lt~obsolete.m4 create mode 100644 src/dbus/.deps/dbus-address.Plo create mode 100644 src/dbus/.deps/dbus-auth-script.Plo create mode 100644 src/dbus/.deps/dbus-auth-util.Plo create mode 100644 src/dbus/.deps/dbus-auth.Plo create mode 100644 src/dbus/.deps/dbus-bus.Plo create mode 100644 src/dbus/.deps/dbus-connection.Plo create mode 100644 src/dbus/.deps/dbus-credentials-util.Plo create mode 100644 src/dbus/.deps/dbus-credentials.Plo create mode 100644 src/dbus/.deps/dbus-dataslot.Plo create mode 100644 src/dbus/.deps/dbus-errors.Plo create mode 100644 src/dbus/.deps/dbus-hash.Plo create mode 100644 src/dbus/.deps/dbus-internals.Plo create mode 100644 src/dbus/.deps/dbus-keyring.Plo create mode 100644 src/dbus/.deps/dbus-list.Plo create mode 100644 src/dbus/.deps/dbus-mainloop.Plo create mode 100644 src/dbus/.deps/dbus-marshal-basic.Plo create mode 100644 src/dbus/.deps/dbus-marshal-byteswap-util.Plo create mode 100644 src/dbus/.deps/dbus-marshal-byteswap.Plo create mode 100644 src/dbus/.deps/dbus-marshal-header.Plo create mode 100644 src/dbus/.deps/dbus-marshal-recursive-util.Plo create mode 100644 src/dbus/.deps/dbus-marshal-recursive.Plo create mode 100644 src/dbus/.deps/dbus-marshal-validate-util.Plo create mode 100644 src/dbus/.deps/dbus-marshal-validate.Plo create mode 100644 src/dbus/.deps/dbus-memory.Plo create mode 100644 src/dbus/.deps/dbus-mempool.Plo create mode 100644 src/dbus/.deps/dbus-message-factory.Plo create mode 100644 src/dbus/.deps/dbus-message-util.Plo create mode 100644 src/dbus/.deps/dbus-message.Plo create mode 100644 src/dbus/.deps/dbus-misc.Plo create mode 100644 src/dbus/.deps/dbus-object-tree.Plo create mode 100644 src/dbus/.deps/dbus-pending-call.Plo create mode 100644 src/dbus/.deps/dbus-resources.Plo create mode 100644 src/dbus/.deps/dbus-server-debug-pipe.Plo create mode 100644 src/dbus/.deps/dbus-server-socket.Plo create mode 100644 src/dbus/.deps/dbus-server-unix.Plo create mode 100644 src/dbus/.deps/dbus-server.Plo create mode 100644 src/dbus/.deps/dbus-sha.Plo create mode 100644 src/dbus/.deps/dbus-shell.Plo create mode 100644 src/dbus/.deps/dbus-signature.Plo create mode 100644 src/dbus/.deps/dbus-spawn.Plo create mode 100644 src/dbus/.deps/dbus-string-util.Plo create mode 100644 src/dbus/.deps/dbus-string.Plo create mode 100644 src/dbus/.deps/dbus-sysdeps-pthread.Plo create mode 100644 src/dbus/.deps/dbus-sysdeps-unix.Plo create mode 100644 src/dbus/.deps/dbus-sysdeps-util-unix.Plo create mode 100644 src/dbus/.deps/dbus-sysdeps-util.Plo create mode 100644 src/dbus/.deps/dbus-sysdeps.Plo create mode 100644 src/dbus/.deps/dbus-test.Plo create mode 100644 src/dbus/.deps/dbus-threads.Plo create mode 100644 src/dbus/.deps/dbus-timeout.Plo create mode 100644 src/dbus/.deps/dbus-transport-socket.Plo create mode 100644 src/dbus/.deps/dbus-transport-unix.Plo create mode 100644 src/dbus/.deps/dbus-transport.Plo create mode 100644 src/dbus/.deps/dbus-userdb-util.Plo create mode 100644 src/dbus/.deps/dbus-userdb.Plo create mode 100644 src/dbus/.deps/dbus-uuidgen.Plo create mode 100644 src/dbus/.deps/dbus-watch.Plo create mode 100644 src/dbus/Makefile create mode 100644 src/dbus/Makefile.am create mode 100644 src/dbus/Makefile.in create mode 100644 src/dbus/dbus-address.c create mode 100644 src/dbus/dbus-address.h create mode 100644 src/dbus/dbus-arch-deps.h create mode 100644 src/dbus/dbus-arch-deps.h.in create mode 100644 src/dbus/dbus-auth-script.c create mode 100644 src/dbus/dbus-auth-script.h create mode 100644 src/dbus/dbus-auth-util.c create mode 100644 src/dbus/dbus-auth.c create mode 100644 src/dbus/dbus-auth.h create mode 100644 src/dbus/dbus-bus.c create mode 100644 src/dbus/dbus-bus.h create mode 100644 src/dbus/dbus-connection-internal.h create mode 100644 src/dbus/dbus-connection.c create mode 100644 src/dbus/dbus-connection.h create mode 100644 src/dbus/dbus-credentials-util.c create mode 100644 src/dbus/dbus-credentials.c create mode 100644 src/dbus/dbus-credentials.h create mode 100644 src/dbus/dbus-dataslot.c create mode 100644 src/dbus/dbus-dataslot.h create mode 100644 src/dbus/dbus-errors.c create mode 100644 src/dbus/dbus-errors.h create mode 100644 src/dbus/dbus-hash.c create mode 100644 src/dbus/dbus-hash.h create mode 100644 src/dbus/dbus-internals.c create mode 100644 src/dbus/dbus-internals.h create mode 100644 src/dbus/dbus-keyring.c create mode 100644 src/dbus/dbus-keyring.h create mode 100644 src/dbus/dbus-list.c create mode 100644 src/dbus/dbus-list.h create mode 100644 src/dbus/dbus-macros.h create mode 100644 src/dbus/dbus-mainloop.c create mode 100644 src/dbus/dbus-mainloop.h create mode 100644 src/dbus/dbus-marshal-basic.c create mode 100644 src/dbus/dbus-marshal-basic.h create mode 100644 src/dbus/dbus-marshal-byteswap-util.c create mode 100644 src/dbus/dbus-marshal-byteswap.c create mode 100644 src/dbus/dbus-marshal-byteswap.h create mode 100644 src/dbus/dbus-marshal-header.c create mode 100644 src/dbus/dbus-marshal-header.h create mode 100644 src/dbus/dbus-marshal-recursive-util.c create mode 100644 src/dbus/dbus-marshal-recursive.c create mode 100644 src/dbus/dbus-marshal-recursive.h create mode 100644 src/dbus/dbus-marshal-validate-util.c create mode 100644 src/dbus/dbus-marshal-validate.c create mode 100644 src/dbus/dbus-marshal-validate.h create mode 100644 src/dbus/dbus-memory.c create mode 100644 src/dbus/dbus-memory.h create mode 100644 src/dbus/dbus-mempool.c create mode 100644 src/dbus/dbus-mempool.h create mode 100644 src/dbus/dbus-message-factory.c create mode 100644 src/dbus/dbus-message-factory.h create mode 100644 src/dbus/dbus-message-internal.h create mode 100644 src/dbus/dbus-message-private.h create mode 100644 src/dbus/dbus-message-util.c create mode 100644 src/dbus/dbus-message.c create mode 100644 src/dbus/dbus-message.h create mode 100644 src/dbus/dbus-misc.c create mode 100644 src/dbus/dbus-misc.h create mode 100644 src/dbus/dbus-object-tree.c create mode 100644 src/dbus/dbus-object-tree.h create mode 100644 src/dbus/dbus-pending-call-internal.h create mode 100644 src/dbus/dbus-pending-call.c create mode 100644 src/dbus/dbus-pending-call.h create mode 100644 src/dbus/dbus-protocol.h create mode 100644 src/dbus/dbus-resources.c create mode 100644 src/dbus/dbus-resources.h create mode 100644 src/dbus/dbus-server-debug-pipe.c create mode 100644 src/dbus/dbus-server-debug-pipe.h create mode 100644 src/dbus/dbus-server-protected.h create mode 100644 src/dbus/dbus-server-socket.c create mode 100644 src/dbus/dbus-server-socket.h create mode 100644 src/dbus/dbus-server-unix.c create mode 100644 src/dbus/dbus-server-unix.h create mode 100644 src/dbus/dbus-server.c create mode 100644 src/dbus/dbus-server.h create mode 100644 src/dbus/dbus-sha.c create mode 100644 src/dbus/dbus-sha.h create mode 100644 src/dbus/dbus-shared.h create mode 100644 src/dbus/dbus-shell.c create mode 100644 src/dbus/dbus-shell.h create mode 100644 src/dbus/dbus-signature.c create mode 100644 src/dbus/dbus-signature.h create mode 100644 src/dbus/dbus-spawn.c create mode 100644 src/dbus/dbus-spawn.h create mode 100644 src/dbus/dbus-string-private.h create mode 100644 src/dbus/dbus-string-util.c create mode 100644 src/dbus/dbus-string.c create mode 100644 src/dbus/dbus-string.h create mode 100644 src/dbus/dbus-sysdeps-pthread.c create mode 100644 src/dbus/dbus-sysdeps-unix.c create mode 100644 src/dbus/dbus-sysdeps-unix.h create mode 100644 src/dbus/dbus-sysdeps-util-unix.c create mode 100644 src/dbus/dbus-sysdeps-util.c create mode 100644 src/dbus/dbus-sysdeps.c create mode 100644 src/dbus/dbus-sysdeps.h create mode 100644 src/dbus/dbus-test-main.c create mode 100644 src/dbus/dbus-test.c create mode 100644 src/dbus/dbus-test.h create mode 100644 src/dbus/dbus-threads-internal.h create mode 100644 src/dbus/dbus-threads.c create mode 100644 src/dbus/dbus-threads.h create mode 100644 src/dbus/dbus-timeout.c create mode 100644 src/dbus/dbus-timeout.h create mode 100644 src/dbus/dbus-transport-protected.h create mode 100644 src/dbus/dbus-transport-socket.c create mode 100644 src/dbus/dbus-transport-socket.h create mode 100644 src/dbus/dbus-transport-unix.c create mode 100644 src/dbus/dbus-transport-unix.h create mode 100644 src/dbus/dbus-transport.c create mode 100644 src/dbus/dbus-transport.h create mode 100644 src/dbus/dbus-types.h create mode 100644 src/dbus/dbus-userdb-util.c create mode 100644 src/dbus/dbus-userdb.c create mode 100644 src/dbus/dbus-userdb.h create mode 100644 src/dbus/dbus-uuidgen.c create mode 100644 src/dbus/dbus-uuidgen.h create mode 100644 src/dbus/dbus-watch.c create mode 100644 src/dbus/dbus-watch.h create mode 100644 src/dbus/dbus.h delete mode 100644 src/defconfig.h diff --git a/.gitignore b/.gitignore index 080950f..ec23663 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,13 @@ src/conky src/stamp-h1 src/.deps/ src/build.h +.cproject +.project +.settings*/ +*.Po +*.o +*.ex +*.EX src/config.h src/defconfig.h src/*.o diff --git a/INSTALL b/INSTALL index f420f3e..54caf7c 100644 --- a/INSTALL +++ b/INSTALL @@ -1,29 +1,40 @@ +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software +Foundation, Inc. + + This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + Basic Installation ================== These are generic installation instructions. - NOTE: If you use Gentoo, see the README about using the included ebuild. - The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that -you can run in the future to recreate the current configuration, a file -`config.cache' that saves the results of its tests to speed up -reconfiguring, and a file `config.log' containing compiler output -(useful mainly for debugging `configure'). +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. (Caching is +disabled by default to prevent problems with accidental use of stale +cache files.) If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can -be considered for the next release. If at some point `config.cache' -contains results you don't want to keep, you may remove or edit it. +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. - The file `configure.in' is used to create `configure' by a program -called `autoconf'. You only need `configure.in' if you want to change -it or regenerate `configure' using a newer version of `autoconf'. + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You only need +`configure.ac' if you want to change it or regenerate `configure' using +a newer version of `autoconf'. The simplest way to compile this package is: @@ -57,14 +68,16 @@ Compilers and Options ===================== Some systems require unusual options for compilation or linking that -the `configure' script does not know about. You can give `configure' -initial values for variables by setting them in the environment. Using -a Bourne-compatible shell, you can do that on the command line like -this: - CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. -Or on systems that have the `env' program, you can do it like this: - env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix + + *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== @@ -77,11 +90,11 @@ directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. - If you have to use a `make' that does not supports the `VPATH' -variable, you have to compile the package for one architecture at a time -in the source code directory. After you have installed the package for -one architecture, use `make distclean' before reconfiguring for another -architecture. + If you have to use a `make' that does not support the `VPATH' +variable, you have to compile the package for one architecture at a +time in the source code directory. After you have installed the +package for one architecture, use `make distclean' before reconfiguring +for another architecture. Installation Names ================== @@ -124,22 +137,32 @@ you can use the `configure' options `--x-includes=DIR' and Specifying the System Type ========================== - There may be some features `configure' can not figure out -automatically, but needs to determine by the type of host the package -will run on. Usually `configure' can figure that out, but if it prints -a message saying it can not guess the host type, give it the -`--host=TYPE' option. TYPE can either be a short name for the system -type, such as `sun4', or a canonical name with three fields: + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, `configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + CPU-COMPANY-SYSTEM -See the file `config.sub' for the possible values of each field. If +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't -need to know the host type. +need to know the machine type. - If you are building compiler tools for cross-compiling, you can also + If you are _building_ compiler tools for cross-compiling, you should use the `--target=TYPE' option to select the type of system they will -produce code for and the `--build=TYPE' option to select the type of -system on which you are compiling the package. +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. Sharing Defaults ================ @@ -152,20 +175,44 @@ default values for variables like `CC', `cache_file', and `prefix'. `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. -Operation Controls +Defining Variables ================== + Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +will cause the specified gcc to be used as the C compiler (unless it is +overridden in the site shell script). + +`configure' Invocation +====================== + `configure' recognizes the following options to control how it operates. -`--cache-file=FILE' - Use and save the results of the tests in FILE instead of - `./config.cache'. Set FILE to `/dev/null' to disable caching, for - debugging `configure'. - `--help' +`-h' Print a summary of the options to `configure', and exit. +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + `--quiet' `--silent' `-q' @@ -177,8 +224,6 @@ operates. Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. -`--version' - Print the version of Autoconf used to generate the `configure' - script, and exit. +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. -`configure' also accepts some other, not widely useful, options. diff --git a/build_notes.txt b/build_notes.txt new file mode 100644 index 0000000..69ce6b8 --- /dev/null +++ b/build_notes.txt @@ -0,0 +1,38 @@ +apt-get install maemo-optify +also need to satisfy liblua5.1-0 by installing liblua5.1-0-dev +x86: +The following extra packages will be installed: + liblua5.1-0 libreadline5-dev +Recommended packages: + libtool +The following packages will be REMOVED: + libreadline4-dev maemo-core-debug maemo-core-dev maemo-sdk-debug + maemo-sdk-dev +The following NEW packages will be installed: + liblua5.1-0 liblua5.1-0-dev libreadline5-dev +armel: +The following extra packages will be installed: + libreadline5 libreadline5-dev readline-common +Recommended packages: + libtool +The following packages will be REMOVED: + libreadline4-dev maemo-core-dev maemo-sdk-dev +The following NEW packages will be installed: + liblua5.1-0-dev libreadline5 libreadline5-dev readline-common +0 upgraded, 4 newly installed, 3 to remove and 8 not upgraded. + +autoreconf --force --install +./configure (--enable-imlib2 ... ??) +make install +make dist +tar xfz conky-1.8.0.tar.gz +cd conky-1.8.0 +dh_make -e lance.colton@gmail.com -f ../conky-1.8.0.tar.gz -c GPL +extract and copy to new debian folder from old one: +control, rules, conky.conf, conky-all.postinst conkylogo48.png, conkylogo64.png, conky.desktop (might not show extension), conky.sh, optify + +remove nvidia-settings from dependancies in control, if x86 + +copy to conky/conky/doc folder: conky.1 + +dpkg-buildpackage -b -rfakeroot -klance.colton@gmail.com diff --git a/config.guess b/config.guess index e3a2116..f32079a 100644 --- a/config.guess +++ b/config.guess @@ -1,10 +1,10 @@ #! /bin/sh # Attempt to guess a canonical system name. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, -# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 +# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 # Free Software Foundation, Inc. -timestamp='2009-06-10' +timestamp='2008-01-23' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -170,7 +170,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval $set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ - | grep -q __ELF__ + | grep __ELF__ >/dev/null then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? @@ -324,9 +324,6 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7; exit ;; esac ;; - s390x:SunOS:*:*) - echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; @@ -334,20 +331,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) - eval $set_cc_for_build - SUN_ARCH="i386" - # If there is a compiler, see if it is configured for 64-bit objects. - # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. - # This test works for both compilers. - if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then - if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_64BIT_ARCH >/dev/null - then - SUN_ARCH="x86_64" - fi - fi - echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize @@ -656,7 +640,7 @@ EOF # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | - grep -q __LP64__ + grep __LP64__ >/dev/null then HP_ARCH="hppa2.0w" else @@ -812,7 +796,7 @@ EOF x86) echo i586-pc-interix${UNAME_RELEASE} exit ;; - EM64T | authenticamd | genuineintel) + EM64T | authenticamd) echo x86_64-unknown-interix${UNAME_RELEASE} exit ;; IA64) @@ -822,9 +806,6 @@ EOF [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) echo i${UNAME_MACHINE}-pc-mks exit ;; - 8664:Windows_NT:*) - echo x86_64-pc-mks - exit ;; i*:Windows_NT*:* | Pentium*:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we @@ -885,17 +866,40 @@ EOF m68*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; - mips:Linux:*:* | mips64:Linux:*:*) + mips:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU - #undef ${UNAME_MACHINE} - #undef ${UNAME_MACHINE}el + #undef mips + #undef mipsel #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) - CPU=${UNAME_MACHINE}el + CPU=mipsel #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) - CPU=${UNAME_MACHINE} + CPU=mips + #else + CPU= + #endif + #endif +EOF + eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n ' + /^CPU/{ + s: ::g + p + }'`" + test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } + ;; + mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef mips64 + #undef mips64el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=mips64el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=mips64 #else CPU= #endif @@ -927,13 +931,10 @@ EOF EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac - objdump --private-headers /bin/sh | grep -q ld.so.1 + objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} exit ;; - padre:Linux:*:*) - echo sparc-unknown-linux-gnu - exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in @@ -981,6 +982,17 @@ EOF elf32-i386) TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" ;; + a.out-i386-linux) + echo "${UNAME_MACHINE}-pc-linux-gnuaout" + exit ;; + coff-i386) + echo "${UNAME_MACHINE}-pc-linux-gnucoff" + exit ;; + "") + # Either a pre-BFD a.out linker (linux-gnuoldld) or + # one that does not give us useful --help. + echo "${UNAME_MACHINE}-pc-linux-gnuoldld" + exit ;; esac # Determine whether the default compiler is a.out or elf eval $set_cc_for_build @@ -1046,7 +1058,7 @@ EOF i*86:syllable:*:*) echo ${UNAME_MACHINE}-pc-syllable exit ;; - i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) echo i386-unknown-lynxos${UNAME_RELEASE} exit ;; i*86:*DOS:*:*) @@ -1090,11 +1102,8 @@ EOF pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about - # the processor, so we play safe by assuming i586. - # Note: whatever this is, it MUST be the same as what config.sub - # prints for the "djgpp" host, or else GDB configury will decide that - # this is a cross-build. - echo i586-pc-msdosdjgpp + # the processor, so we play safe by assuming i386. + echo i386-pc-msdosdjgpp exit ;; Intel:Mach:3*:*) echo i386-pc-mach3 @@ -1132,16 +1141,6 @@ EOF 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; - NCR*:*:4.2:* | MPRAS*:*:4.2:*) - OS_REL='.3' - test -r /etc/.relid \ - && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` - /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4.3${OS_REL}; exit; } - /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } - /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos${UNAME_RELEASE} exit ;; @@ -1154,7 +1153,7 @@ EOF rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos${UNAME_RELEASE} exit ;; - PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) echo powerpc-unknown-lynxos${UNAME_RELEASE} exit ;; SM[BE]S:UNIX_SV:*:*) @@ -1217,9 +1216,6 @@ EOF BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit ;; - BePC:Haiku:*:*) # Haiku running on Intel PC compatible. - echo i586-pc-haiku - exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux${UNAME_RELEASE} exit ;; @@ -1328,9 +1324,6 @@ EOF i*86:rdos:*:*) echo ${UNAME_MACHINE}-pc-rdos exit ;; - i*86:AROS:*:*) - echo ${UNAME_MACHINE}-pc-aros - exit ;; esac #echo '(No uname command or uname output not recognized.)' 1>&2 diff --git a/config.sub b/config.sub index eb0389a..6759825 100644 --- a/config.sub +++ b/config.sub @@ -1,10 +1,10 @@ #! /bin/sh # Configuration validation subroutine script. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, -# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 +# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 # Free Software Foundation, Inc. -timestamp='2009-06-11' +timestamp='2008-01-16' # This file is (in principle) common to ALL GNU software. # The presence of a machine in this file suggests that SOME GNU software @@ -122,7 +122,6 @@ maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \ uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \ - kopensolaris*-gnu* | \ storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` @@ -153,9 +152,6 @@ case $os in os= basic_machine=$1 ;; - -bluegene*) - os=-cnk - ;; -sim | -cisco | -oki | -wec | -winbond) os= basic_machine=$1 @@ -253,16 +249,13 @@ case $basic_machine in | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | i370 | i860 | i960 | ia64 \ | ip2k | iq2000 \ - | lm32 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ - | maxq | mb | microblaze | mcore | mep | metag \ + | maxq | mb | microblaze | mcore | mep \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ - | mips64octeon | mips64octeonel \ - | mips64orion | mips64orionel \ - | mips64r5900 | mips64r5900el \ | mips64vr | mips64vrel \ + | mips64orion | mips64orionel \ | mips64vr4100 | mips64vr4100el \ | mips64vr4300 | mips64vr4300el \ | mips64vr5000 | mips64vr5000el \ @@ -275,7 +268,6 @@ case $basic_machine in | mipsisa64sr71k | mipsisa64sr71kel \ | mipstx39 | mipstx39el \ | mn10200 | mn10300 \ - | moxie \ | mt \ | msp430 \ | nios | nios2 \ @@ -285,7 +277,7 @@ case $basic_machine in | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ | pyramid \ | score \ - | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ + | sh | sh[1234] | sh[24]a | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ @@ -294,7 +286,7 @@ case $basic_machine in | v850 | v850e \ | we32k \ | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \ - | z8k | z80) + | z8k) basic_machine=$basic_machine-unknown ;; m6811 | m68hc11 | m6812 | m68hc12) @@ -337,17 +329,14 @@ case $basic_machine in | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | i*86-* | i860-* | i960-* | ia64-* \ | ip2k-* | iq2000-* \ - | lm32-* \ | m32c-* | m32r-* | m32rle-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ - | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ + | m88110-* | m88k-* | maxq-* | mcore-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ - | mips64octeon-* | mips64octeonel-* \ - | mips64orion-* | mips64orionel-* \ - | mips64r5900-* | mips64r5900el-* \ | mips64vr-* | mips64vrel-* \ + | mips64orion-* | mips64orionel-* \ | mips64vr4100-* | mips64vr4100el-* \ | mips64vr4300-* | mips64vr4300el-* \ | mips64vr5000-* | mips64vr5000el-* \ @@ -369,20 +358,20 @@ case $basic_machine in | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ | pyramid-* \ | romp-* | rs6000-* \ - | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ + | sh-* | sh[1234]-* | sh[24]a-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ | sparclite-* \ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \ | tahoe-* | thumb-* \ - | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* | tile-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tron-* \ | v850-* | v850e-* | vax-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \ | xstormy16-* | xtensa*-* \ | ymp-* \ - | z8k-* | z80-*) + | z8k-*) ;; # Recognize the basic CPU types without company name, with glob match. xtensa*) @@ -450,10 +439,6 @@ case $basic_machine in basic_machine=m68k-apollo os=-bsd ;; - aros) - basic_machine=i386-pc - os=-aros - ;; aux) basic_machine=m68k-apple os=-aux @@ -470,18 +455,10 @@ case $basic_machine in basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; - bluegene*) - basic_machine=powerpc-ibm - os=-cnk - ;; c90) basic_machine=c90-cray os=-unicos ;; - cegcc) - basic_machine=arm-unknown - os=-cegcc - ;; convex-c1) basic_machine=c1-convex os=-bsd @@ -549,10 +526,6 @@ case $basic_machine in basic_machine=m88k-motorola os=-sysv3 ;; - dicos) - basic_machine=i686-pc - os=-dicos - ;; djgpp) basic_machine=i586-pc os=-msdosdjgpp @@ -1155,10 +1128,6 @@ case $basic_machine in basic_machine=z8k-unknown os=-sim ;; - z80-*-coff) - basic_machine=z80-unknown - os=-sim - ;; none) basic_machine=none-none os=-none @@ -1197,7 +1166,7 @@ case $basic_machine in we32k) basic_machine=we32k-att ;; - sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) + sh[1234] | sh[24]a | sh[34]eb | sh[1234]le | sh[23]ele) basic_machine=sh-unknown ;; sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) @@ -1267,11 +1236,10 @@ case $os in # Each alternative MUST END IN A *, to match a version number. # -sysv* is not here because it comes later, after sysvr4. -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ - | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ - | -kopensolaris* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ - | -aos* | -aros* \ + | -aos* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ @@ -1280,7 +1248,7 @@ case $os in | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ - | -chorusos* | -chorusrdb* | -cegcc* \ + | -chorusos* | -chorusrdb* \ | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \ | -uxpv* | -beos* | -mpeix* | -udk* \ @@ -1420,9 +1388,6 @@ case $os in -zvmoe) os=-zvmoe ;; - -dicos*) - os=-dicos - ;; -none) ;; *) @@ -1620,7 +1585,7 @@ case $basic_machine in -sunos*) vendor=sun ;; - -cnk*|-aix*) + -aix*) vendor=ibm ;; -beos*) diff --git a/configure b/configure index 1f4278a..23fc497 100644 --- a/configure +++ b/configure @@ -20302,7 +20302,7 @@ echo "${ECHO_T}no" >&6; } fi -ac_config_files="$ac_config_files Makefile data/Makefile doc/Makefile src/Makefile src/build.h lua/Makefile" +ac_config_files="$ac_config_files Makefile data/Makefile doc/Makefile src/Makefile src/build.h lua/Makefile src/dbus/Makefile" uname=`uname` @@ -29684,6 +29684,7 @@ do "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; "src/build.h") CONFIG_FILES="$CONFIG_FILES src/build.h" ;; "lua/Makefile") CONFIG_FILES="$CONFIG_FILES lua/Makefile" ;; + "src/dbus/Makefile") CONFIG_FILES="$CONFIG_FILES src/dbus/Makefile" ;; *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 echo "$as_me: error: invalid argument: $ac_config_target" >&2;} diff --git a/configure.ac b/configure.ac index 945ed96..21ff0cd 100644 --- a/configure.ac +++ b/configure.ac @@ -49,6 +49,7 @@ AC_CONFIG_FILES( src/Makefile src/build.h lua/Makefile + src/dbus/Makefile ) uname=`uname` diff --git a/configure.ac.in b/configure.ac.in index 289c5bb..8a51790 100644 --- a/configure.ac.in +++ b/configure.ac.in @@ -49,6 +49,7 @@ AC_CONFIG_FILES( src/Makefile src/build.h lua/Makefile + src/dbus/Makefile ) uname=`uname` diff --git a/debian/README.Debian b/debian/README.Debian new file mode 100644 index 0000000..82227d4 --- /dev/null +++ b/debian/README.Debian @@ -0,0 +1,6 @@ +conky for Debian +---------------- + + + + -- Lance Colton Mon, 27 Sep 2010 23:05:20 -0700 diff --git a/debian/control b/debian/control index ee71d5d..a0a4c4a 100644 --- a/debian/control +++ b/debian/control @@ -11,7 +11,7 @@ Build-Depends: debhelper (>= 5.0.0), libtool, automake, libasound2-dev [!kfreebsd-i386 !kfreebsd-amd64 !hurd-i386], libkvm-dev [kfreebsd-i386 kfreebsd-amd64], libdevstat-dev [kfreebsd-i386 kfreebsd-amd64], - nvidia-settings [i386 amd64], libncurses5-dev + libncurses5-dev, liblua5.1-0-dev, lib-dbus-1-dev Homepage: http://conky.sourceforge.net/ Standards-Version: 3.8.4 @@ -70,7 +70,7 @@ Description: Highly configurable system monitor (transitional package) Package: conky-all Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends} +Depends: ${shlibs:Depends}, ${misc:Depends}, liblua5.1-0 Suggests: apcupsd, moc, mpd Conflicts: conky-std, conky-cli Replaces: conky, conky-std, conky-cli diff --git a/debian/dirs b/debian/dirs new file mode 100644 index 0000000..ca882bb --- /dev/null +++ b/debian/dirs @@ -0,0 +1,2 @@ +usr/bin +usr/sbin diff --git a/debian/docs b/debian/docs new file mode 100644 index 0000000..5502ed8 --- /dev/null +++ b/debian/docs @@ -0,0 +1,3 @@ +NEWS +README +TODO diff --git a/debian/files b/debian/files deleted file mode 100644 index 1fbfa90..0000000 --- a/debian/files +++ /dev/null @@ -1,2 +0,0 @@ -conky_1.8.0-1_all.deb user/desktop extra -conky-all_1.8.0-1_armel.deb user/desktop optional diff --git a/debian/rules b/debian/rules index cd528af..48ff3a4 100644 --- a/debian/rules +++ b/debian/rules @@ -2,7 +2,7 @@ # -*- makefile -*- # Uncomment this to turn on verbose mode. -#export DH_VERBOSE=1 +export DH_VERBOSE=1 # These are used for cross-compiling and for saving the configure script # from having to guess our platform (since we know it already) @@ -12,7 +12,7 @@ DEB_HOST_ARCH_OS ?= $(shell dpkg-architecture -qDEB_HOST_ARCH_OS) DEB_HOST_ARCH_CPU ?= $(shell dpkg-architecture -qDEB_HOST_ARCH_CPU) CFLAGS = -Wall -g -LDFLAGS = -Wl,--as-needed +LDFLAGS = -Wl,--as-needed -ldbus-1 ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) CFLAGS += -O0 @@ -44,14 +44,14 @@ configure: config-stamp config-stamp: dh_testdir - - chmod +x ./autogen.sh +#DML# chmod +x ./autogen.sh AUTOMAKE=automake ./autogen.sh - ln -sf /usr/share/misc/config.sub . - ln -sf /usr/share/misc/config.guess . +# copy these instead +# ln -sf /usr/share/misc/config.sub . +# ln -sf /usr/share/misc/config.guess . - mkdir build-std build-cli build-all + mkdir build-all #DML# cd build-std && CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" \ #DML# ../configure $(COMMON_CONFIGURE_FLAGS) $(LINUX_CONF_ARGS_STD) @@ -65,7 +65,7 @@ config-stamp: cd build-all && CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" \ ../configure $(COMMON_CONFIGURE_FLAGS) \ - --enable-imlib2=no --enable-rss --enable-weather-xoap \ + --enable-rss --enable-weather-xoap \ --enable-eve --enable-lua=no --enable-lua-cairo=no --enable-lua-imlib2=no \ --disable-static --enable-argb \ $(LINUX_CONF_ARGS_ALL) $(ENABLE_NVIDIA) diff --git a/m4/libtool.m4 b/m4/libtool.m4 deleted file mode 100644 index a3fee53..0000000 --- a/m4/libtool.m4 +++ /dev/null @@ -1,7377 +0,0 @@ -# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- -# -# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, -# 2006, 2007, 2008 Free Software Foundation, Inc. -# Written by Gordon Matzigkeit, 1996 -# -# This file 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. - -m4_define([_LT_COPYING], [dnl -# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, -# 2006, 2007, 2008 Free Software Foundation, Inc. -# Written by Gordon Matzigkeit, 1996 -# -# This file is part of GNU Libtool. -# -# GNU Libtool 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. -# -# As a special exception to the GNU General Public License, -# if you distribute this file as part of a program or library that -# is built using GNU Libtool, you may include this file under the -# same distribution terms that you use for the rest of that program. -# -# GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy -# can be downloaded from http://www.gnu.org/licenses/gpl.html, or -# obtained by writing to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -]) - -# serial 56 LT_INIT - - -# LT_PREREQ(VERSION) -# ------------------ -# Complain and exit if this libtool version is less that VERSION. -m4_defun([LT_PREREQ], -[m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1, - [m4_default([$3], - [m4_fatal([Libtool version $1 or higher is required], - 63)])], - [$2])]) - - -# _LT_CHECK_BUILDDIR -# ------------------ -# Complain if the absolute build directory name contains unusual characters -m4_defun([_LT_CHECK_BUILDDIR], -[case `pwd` in - *\ * | *\ *) - AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;; -esac -]) - - -# LT_INIT([OPTIONS]) -# ------------------ -AC_DEFUN([LT_INIT], -[AC_PREREQ([2.58])dnl We use AC_INCLUDES_DEFAULT -AC_BEFORE([$0], [LT_LANG])dnl -AC_BEFORE([$0], [LT_OUTPUT])dnl -AC_BEFORE([$0], [LTDL_INIT])dnl -m4_require([_LT_CHECK_BUILDDIR])dnl - -dnl Autoconf doesn't catch unexpanded LT_ macros by default: -m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl -m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl -dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4 -dnl unless we require an AC_DEFUNed macro: -AC_REQUIRE([LTOPTIONS_VERSION])dnl -AC_REQUIRE([LTSUGAR_VERSION])dnl -AC_REQUIRE([LTVERSION_VERSION])dnl -AC_REQUIRE([LTOBSOLETE_VERSION])dnl -m4_require([_LT_PROG_LTMAIN])dnl - -dnl Parse OPTIONS -_LT_SET_OPTIONS([$0], [$1]) - -# This can be used to rebuild libtool when needed -LIBTOOL_DEPS="$ltmain" - -# Always use our own libtool. -LIBTOOL='$(SHELL) $(top_builddir)/libtool' -AC_SUBST(LIBTOOL)dnl - -_LT_SETUP - -# Only expand once: -m4_define([LT_INIT]) -])# LT_INIT - -# Old names: -AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT]) -AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT]) -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([AC_PROG_LIBTOOL], []) -dnl AC_DEFUN([AM_PROG_LIBTOOL], []) - - -# _LT_CC_BASENAME(CC) -# ------------------- -# Calculate cc_basename. Skip known compiler wrappers and cross-prefix. -m4_defun([_LT_CC_BASENAME], -[for cc_temp in $1""; do - case $cc_temp in - compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;; - distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;; - \-*) ;; - *) break;; - esac -done -cc_basename=`$ECHO "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"` -]) - - -# _LT_FILEUTILS_DEFAULTS -# ---------------------- -# It is okay to use these file commands and assume they have been set -# sensibly after `m4_require([_LT_FILEUTILS_DEFAULTS])'. -m4_defun([_LT_FILEUTILS_DEFAULTS], -[: ${CP="cp -f"} -: ${MV="mv -f"} -: ${RM="rm -f"} -])# _LT_FILEUTILS_DEFAULTS - - -# _LT_SETUP -# --------- -m4_defun([_LT_SETUP], -[AC_REQUIRE([AC_CANONICAL_HOST])dnl -AC_REQUIRE([AC_CANONICAL_BUILD])dnl -_LT_DECL([], [host_alias], [0], [The host system])dnl -_LT_DECL([], [host], [0])dnl -_LT_DECL([], [host_os], [0])dnl -dnl -_LT_DECL([], [build_alias], [0], [The build system])dnl -_LT_DECL([], [build], [0])dnl -_LT_DECL([], [build_os], [0])dnl -dnl -AC_REQUIRE([AC_PROG_CC])dnl -AC_REQUIRE([LT_PATH_LD])dnl -AC_REQUIRE([LT_PATH_NM])dnl -dnl -AC_REQUIRE([AC_PROG_LN_S])dnl -test -z "$LN_S" && LN_S="ln -s" -_LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl -dnl -AC_REQUIRE([LT_CMD_MAX_LEN])dnl -_LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl -_LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl -dnl -m4_require([_LT_FILEUTILS_DEFAULTS])dnl -m4_require([_LT_CHECK_SHELL_FEATURES])dnl -m4_require([_LT_CMD_RELOAD])dnl -m4_require([_LT_CHECK_MAGIC_METHOD])dnl -m4_require([_LT_CMD_OLD_ARCHIVE])dnl -m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl - -_LT_CONFIG_LIBTOOL_INIT([ -# See if we are running on zsh, and set the options which allow our -# commands through without removal of \ escapes INIT. -if test -n "\${ZSH_VERSION+set}" ; then - setopt NO_GLOB_SUBST -fi -]) -if test -n "${ZSH_VERSION+set}" ; then - setopt NO_GLOB_SUBST -fi - -_LT_CHECK_OBJDIR - -m4_require([_LT_TAG_COMPILER])dnl -_LT_PROG_ECHO_BACKSLASH - -case $host_os in -aix3*) - # AIX sometimes has problems with the GCC collect2 program. For some - # reason, if we set the COLLECT_NAMES environment variable, the problems - # vanish in a puff of smoke. - if test "X${COLLECT_NAMES+set}" != Xset; then - COLLECT_NAMES= - export COLLECT_NAMES - fi - ;; -esac - -# Sed substitution that helps us do robust quoting. It backslashifies -# metacharacters that are still active within double-quoted strings. -sed_quote_subst='s/\([["`$\\]]\)/\\\1/g' - -# Same as above, but do not quote variable references. -double_quote_subst='s/\([["`\\]]\)/\\\1/g' - -# Sed substitution to delay expansion of an escaped shell variable in a -# double_quote_subst'ed string. -delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' - -# Sed substitution to delay expansion of an escaped single quote. -delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' - -# Sed substitution to avoid accidental globbing in evaled expressions -no_glob_subst='s/\*/\\\*/g' - -# Global variables: -ofile=libtool -can_build_shared=yes - -# All known linkers require a `.a' archive for static linking (except MSVC, -# which needs '.lib'). -libext=a - -with_gnu_ld="$lt_cv_prog_gnu_ld" - -old_CC="$CC" -old_CFLAGS="$CFLAGS" - -# Set sane defaults for various variables -test -z "$CC" && CC=cc -test -z "$LTCC" && LTCC=$CC -test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS -test -z "$LD" && LD=ld -test -z "$ac_objext" && ac_objext=o - -_LT_CC_BASENAME([$compiler]) - -# Only perform the check for file, if the check method requires it -test -z "$MAGIC_CMD" && MAGIC_CMD=file -case $deplibs_check_method in -file_magic*) - if test "$file_magic_cmd" = '$MAGIC_CMD'; then - _LT_PATH_MAGIC - fi - ;; -esac - -# Use C for the default configuration in the libtool script -LT_SUPPORTED_TAG([CC]) -_LT_LANG_C_CONFIG -_LT_LANG_DEFAULT_CONFIG -_LT_CONFIG_COMMANDS -])# _LT_SETUP - - -# _LT_PROG_LTMAIN -# --------------- -# Note that this code is called both from `configure', and `config.status' -# now that we use AC_CONFIG_COMMANDS to generate libtool. Notably, -# `config.status' has no value for ac_aux_dir unless we are using Automake, -# so we pass a copy along to make sure it has a sensible value anyway. -m4_defun([_LT_PROG_LTMAIN], -[m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl -_LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir']) -ltmain="$ac_aux_dir/ltmain.sh" -])# _LT_PROG_LTMAIN - - -## ------------------------------------- ## -## Accumulate code for creating libtool. ## -## ------------------------------------- ## - -# So that we can recreate a full libtool script including additional -# tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS -# in macros and then make a single call at the end using the `libtool' -# label. - - -# _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS]) -# ---------------------------------------- -# Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later. -m4_define([_LT_CONFIG_LIBTOOL_INIT], -[m4_ifval([$1], - [m4_append([_LT_OUTPUT_LIBTOOL_INIT], - [$1 -])])]) - -# Initialize. -m4_define([_LT_OUTPUT_LIBTOOL_INIT]) - - -# _LT_CONFIG_LIBTOOL([COMMANDS]) -# ------------------------------ -# Register COMMANDS to be passed to AC_CONFIG_COMMANDS later. -m4_define([_LT_CONFIG_LIBTOOL], -[m4_ifval([$1], - [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS], - [$1 -])])]) - -# Initialize. -m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS]) - - -# _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS]) -# ----------------------------------------------------- -m4_defun([_LT_CONFIG_SAVE_COMMANDS], -[_LT_CONFIG_LIBTOOL([$1]) -_LT_CONFIG_LIBTOOL_INIT([$2]) -]) - - -# _LT_FORMAT_COMMENT([COMMENT]) -# ----------------------------- -# Add leading comment marks to the start of each line, and a trailing -# full-stop to the whole comment if one is not present already. -m4_define([_LT_FORMAT_COMMENT], -[m4_ifval([$1], [ -m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])], - [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.]) -)]) - - - -## ------------------------ ## -## FIXME: Eliminate VARNAME ## -## ------------------------ ## - - -# _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?]) -# ------------------------------------------------------------------- -# CONFIGNAME is the name given to the value in the libtool script. -# VARNAME is the (base) name used in the configure script. -# VALUE may be 0, 1 or 2 for a computed quote escaped value based on -# VARNAME. Any other value will be used directly. -m4_define([_LT_DECL], -[lt_if_append_uniq([lt_decl_varnames], [$2], [, ], - [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name], - [m4_ifval([$1], [$1], [$2])]) - lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3]) - m4_ifval([$4], - [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])]) - lt_dict_add_subkey([lt_decl_dict], [$2], - [tagged?], [m4_ifval([$5], [yes], [no])])]) -]) - - -# _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION]) -# -------------------------------------------------------- -m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])]) - - -# lt_decl_tag_varnames([SEPARATOR], [VARNAME1...]) -# ------------------------------------------------ -m4_define([lt_decl_tag_varnames], -[_lt_decl_filter([tagged?], [yes], $@)]) - - -# _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..]) -# --------------------------------------------------------- -m4_define([_lt_decl_filter], -[m4_case([$#], - [0], [m4_fatal([$0: too few arguments: $#])], - [1], [m4_fatal([$0: too few arguments: $#: $1])], - [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)], - [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)], - [lt_dict_filter([lt_decl_dict], $@)])[]dnl -]) - - -# lt_decl_quote_varnames([SEPARATOR], [VARNAME1...]) -# -------------------------------------------------- -m4_define([lt_decl_quote_varnames], -[_lt_decl_filter([value], [1], $@)]) - - -# lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...]) -# --------------------------------------------------- -m4_define([lt_decl_dquote_varnames], -[_lt_decl_filter([value], [2], $@)]) - - -# lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...]) -# --------------------------------------------------- -m4_define([lt_decl_varnames_tagged], -[m4_assert([$# <= 2])dnl -_$0(m4_quote(m4_default([$1], [[, ]])), - m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]), - m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))]) -m4_define([_lt_decl_varnames_tagged], -[m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])]) - - -# lt_decl_all_varnames([SEPARATOR], [VARNAME1...]) -# ------------------------------------------------ -m4_define([lt_decl_all_varnames], -[_$0(m4_quote(m4_default([$1], [[, ]])), - m4_if([$2], [], - m4_quote(lt_decl_varnames), - m4_quote(m4_shift($@))))[]dnl -]) -m4_define([_lt_decl_all_varnames], -[lt_join($@, lt_decl_varnames_tagged([$1], - lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl -]) - - -# _LT_CONFIG_STATUS_DECLARE([VARNAME]) -# ------------------------------------ -# Quote a variable value, and forward it to `config.status' so that its -# declaration there will have the same value as in `configure'. VARNAME -# must have a single quote delimited value for this to work. -m4_define([_LT_CONFIG_STATUS_DECLARE], -[$1='`$ECHO "X$][$1" | $Xsed -e "$delay_single_quote_subst"`']) - - -# _LT_CONFIG_STATUS_DECLARATIONS -# ------------------------------ -# We delimit libtool config variables with single quotes, so when -# we write them to config.status, we have to be sure to quote all -# embedded single quotes properly. In configure, this macro expands -# each variable declared with _LT_DECL (and _LT_TAGDECL) into: -# -# ='`$ECHO "X$" | $Xsed -e "$delay_single_quote_subst"`' -m4_defun([_LT_CONFIG_STATUS_DECLARATIONS], -[m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames), - [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])]) - - -# _LT_LIBTOOL_TAGS -# ---------------- -# Output comment and list of tags supported by the script -m4_defun([_LT_LIBTOOL_TAGS], -[_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl -available_tags="_LT_TAGS"dnl -]) - - -# _LT_LIBTOOL_DECLARE(VARNAME, [TAG]) -# ----------------------------------- -# Extract the dictionary values for VARNAME (optionally with TAG) and -# expand to a commented shell variable setting: -# -# # Some comment about what VAR is for. -# visible_name=$lt_internal_name -m4_define([_LT_LIBTOOL_DECLARE], -[_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], - [description])))[]dnl -m4_pushdef([_libtool_name], - m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl -m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])), - [0], [_libtool_name=[$]$1], - [1], [_libtool_name=$lt_[]$1], - [2], [_libtool_name=$lt_[]$1], - [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl -m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl -]) - - -# _LT_LIBTOOL_CONFIG_VARS -# ----------------------- -# Produce commented declarations of non-tagged libtool config variables -# suitable for insertion in the LIBTOOL CONFIG section of the `libtool' -# script. Tagged libtool config variables (even for the LIBTOOL CONFIG -# section) are produced by _LT_LIBTOOL_TAG_VARS. -m4_defun([_LT_LIBTOOL_CONFIG_VARS], -[m4_foreach([_lt_var], - m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)), - [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])]) - - -# _LT_LIBTOOL_TAG_VARS(TAG) -# ------------------------- -m4_define([_LT_LIBTOOL_TAG_VARS], -[m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames), - [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])]) - - -# _LT_TAGVAR(VARNAME, [TAGNAME]) -# ------------------------------ -m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])]) - - -# _LT_CONFIG_COMMANDS -# ------------------- -# Send accumulated output to $CONFIG_STATUS. Thanks to the lists of -# variables for single and double quote escaping we saved from calls -# to _LT_DECL, we can put quote escaped variables declarations -# into `config.status', and then the shell code to quote escape them in -# for loops in `config.status'. Finally, any additional code accumulated -# from calls to _LT_CONFIG_LIBTOOL_INIT is expanded. -m4_defun([_LT_CONFIG_COMMANDS], -[AC_PROVIDE_IFELSE([LT_OUTPUT], - dnl If the libtool generation code has been placed in $CONFIG_LT, - dnl instead of duplicating it all over again into config.status, - dnl then we will have config.status run $CONFIG_LT later, so it - dnl needs to know what name is stored there: - [AC_CONFIG_COMMANDS([libtool], - [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])], - dnl If the libtool generation code is destined for config.status, - dnl expand the accumulated commands and init code now: - [AC_CONFIG_COMMANDS([libtool], - [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])]) -])#_LT_CONFIG_COMMANDS - - -# Initialize. -m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT], -[ - -# The HP-UX ksh and POSIX shell print the target directory to stdout -# if CDPATH is set. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - -sed_quote_subst='$sed_quote_subst' -double_quote_subst='$double_quote_subst' -delay_variable_subst='$delay_variable_subst' -_LT_CONFIG_STATUS_DECLARATIONS -LTCC='$LTCC' -LTCFLAGS='$LTCFLAGS' -compiler='$compiler_DEFAULT' - -# Quote evaled strings. -for var in lt_decl_all_varnames([[ \ -]], lt_decl_quote_varnames); do - case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in - *[[\\\\\\\`\\"\\\$]]*) - eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" - ;; - *) - eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" - ;; - esac -done - -# Double-quote double-evaled strings. -for var in lt_decl_all_varnames([[ \ -]], lt_decl_dquote_varnames); do - case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in - *[[\\\\\\\`\\"\\\$]]*) - eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" - ;; - *) - eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" - ;; - esac -done - -# Fix-up fallback echo if it was mangled by the above quoting rules. -case \$lt_ECHO in -*'\\\[$]0 --fallback-echo"')dnl " - lt_ECHO=\`\$ECHO "X\$lt_ECHO" | \$Xsed -e 's/\\\\\\\\\\\\\\\[$]0 --fallback-echo"\[$]/\[$]0 --fallback-echo"/'\` - ;; -esac - -_LT_OUTPUT_LIBTOOL_INIT -]) - - -# LT_OUTPUT -# --------- -# This macro allows early generation of the libtool script (before -# AC_OUTPUT is called), incase it is used in configure for compilation -# tests. -AC_DEFUN([LT_OUTPUT], -[: ${CONFIG_LT=./config.lt} -AC_MSG_NOTICE([creating $CONFIG_LT]) -cat >"$CONFIG_LT" <<_LTEOF -#! $SHELL -# Generated by $as_me. -# Run this file to recreate a libtool stub with the current configuration. - -lt_cl_silent=false -SHELL=\${CONFIG_SHELL-$SHELL} -_LTEOF - -cat >>"$CONFIG_LT" <<\_LTEOF -AS_SHELL_SANITIZE -_AS_PREPARE - -exec AS_MESSAGE_FD>&1 -exec AS_MESSAGE_LOG_FD>>config.log -{ - echo - AS_BOX([Running $as_me.]) -} >&AS_MESSAGE_LOG_FD - -lt_cl_help="\ -\`$as_me' creates a local libtool stub from the current configuration, -for use in further configure time tests before the real libtool is -generated. - -Usage: $[0] [[OPTIONS]] - - -h, --help print this help, then exit - -V, --version print version number, then exit - -q, --quiet do not print progress messages - -d, --debug don't remove temporary files - -Report bugs to ." - -lt_cl_version="\ -m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl -m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION]) -configured by $[0], generated by m4_PACKAGE_STRING. - -Copyright (C) 2008 Free Software Foundation, Inc. -This config.lt script is free software; the Free Software Foundation -gives unlimited permision to copy, distribute and modify it." - -while test $[#] != 0 -do - case $[1] in - --version | --v* | -V ) - echo "$lt_cl_version"; exit 0 ;; - --help | --h* | -h ) - echo "$lt_cl_help"; exit 0 ;; - --debug | --d* | -d ) - debug=: ;; - --quiet | --q* | --silent | --s* | -q ) - lt_cl_silent=: ;; - - -*) AC_MSG_ERROR([unrecognized option: $[1] -Try \`$[0] --help' for more information.]) ;; - - *) AC_MSG_ERROR([unrecognized argument: $[1] -Try \`$[0] --help' for more information.]) ;; - esac - shift -done - -if $lt_cl_silent; then - exec AS_MESSAGE_FD>/dev/null -fi -_LTEOF - -cat >>"$CONFIG_LT" <<_LTEOF -_LT_OUTPUT_LIBTOOL_COMMANDS_INIT -_LTEOF - -cat >>"$CONFIG_LT" <<\_LTEOF -AC_MSG_NOTICE([creating $ofile]) -_LT_OUTPUT_LIBTOOL_COMMANDS -AS_EXIT(0) -_LTEOF -chmod +x "$CONFIG_LT" - -# configure is writing to config.log, but config.lt does its own redirection, -# appending to config.log, which fails on DOS, as config.log is still kept -# open by configure. Here we exec the FD to /dev/null, effectively closing -# config.log, so it can be properly (re)opened and appended to by config.lt. -if test "$no_create" != yes; then - lt_cl_success=: - test "$silent" = yes && - lt_config_lt_args="$lt_config_lt_args --quiet" - exec AS_MESSAGE_LOG_FD>/dev/null - $SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false - exec AS_MESSAGE_LOG_FD>>config.log - $lt_cl_success || AS_EXIT(1) -fi -])# LT_OUTPUT - - -# _LT_CONFIG(TAG) -# --------------- -# If TAG is the built-in tag, create an initial libtool script with a -# default configuration from the untagged config vars. Otherwise add code -# to config.status for appending the configuration named by TAG from the -# matching tagged config vars. -m4_defun([_LT_CONFIG], -[m4_require([_LT_FILEUTILS_DEFAULTS])dnl -_LT_CONFIG_SAVE_COMMANDS([ - m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl - m4_if(_LT_TAG, [C], [ - # See if we are running on zsh, and set the options which allow our - # commands through without removal of \ escapes. - if test -n "${ZSH_VERSION+set}" ; then - setopt NO_GLOB_SUBST - fi - - cfgfile="${ofile}T" - trap "$RM \"$cfgfile\"; exit 1" 1 2 15 - $RM "$cfgfile" - - cat <<_LT_EOF >> "$cfgfile" -#! $SHELL - -# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services. -# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION -# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: -# NOTE: Changes made to this file will be lost: look at ltmain.sh. -# -_LT_COPYING -_LT_LIBTOOL_TAGS - -# ### BEGIN LIBTOOL CONFIG -_LT_LIBTOOL_CONFIG_VARS -_LT_LIBTOOL_TAG_VARS -# ### END LIBTOOL CONFIG - -_LT_EOF - - case $host_os in - aix3*) - cat <<\_LT_EOF >> "$cfgfile" -# AIX sometimes has problems with the GCC collect2 program. For some -# reason, if we set the COLLECT_NAMES environment variable, the problems -# vanish in a puff of smoke. -if test "X${COLLECT_NAMES+set}" != Xset; then - COLLECT_NAMES= - export COLLECT_NAMES -fi -_LT_EOF - ;; - esac - - _LT_PROG_LTMAIN - - # We use sed instead of cat because bash on DJGPP gets confused if - # if finds mixed CR/LF and LF-only lines. Since sed operates in - # text mode, it properly converts lines to CR/LF. This bash problem - # is reportedly fixed, but why not run on old versions too? - sed '/^# Generated shell functions inserted here/q' "$ltmain" >> "$cfgfile" \ - || (rm -f "$cfgfile"; exit 1) - - _LT_PROG_XSI_SHELLFNS - - sed -n '/^# Generated shell functions inserted here/,$p' "$ltmain" >> "$cfgfile" \ - || (rm -f "$cfgfile"; exit 1) - - mv -f "$cfgfile" "$ofile" || - (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") - chmod +x "$ofile" -], -[cat <<_LT_EOF >> "$ofile" - -dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded -dnl in a comment (ie after a #). -# ### BEGIN LIBTOOL TAG CONFIG: $1 -_LT_LIBTOOL_TAG_VARS(_LT_TAG) -# ### END LIBTOOL TAG CONFIG: $1 -_LT_EOF -])dnl /m4_if -], -[m4_if([$1], [], [ - PACKAGE='$PACKAGE' - VERSION='$VERSION' - TIMESTAMP='$TIMESTAMP' - RM='$RM' - ofile='$ofile'], []) -])dnl /_LT_CONFIG_SAVE_COMMANDS -])# _LT_CONFIG - - -# LT_SUPPORTED_TAG(TAG) -# --------------------- -# Trace this macro to discover what tags are supported by the libtool -# --tag option, using: -# autoconf --trace 'LT_SUPPORTED_TAG:$1' -AC_DEFUN([LT_SUPPORTED_TAG], []) - - -# C support is built-in for now -m4_define([_LT_LANG_C_enabled], []) -m4_define([_LT_TAGS], []) - - -# LT_LANG(LANG) -# ------------- -# Enable libtool support for the given language if not already enabled. -AC_DEFUN([LT_LANG], -[AC_BEFORE([$0], [LT_OUTPUT])dnl -m4_case([$1], - [C], [_LT_LANG(C)], - [C++], [_LT_LANG(CXX)], - [Java], [_LT_LANG(GCJ)], - [Fortran 77], [_LT_LANG(F77)], - [Fortran], [_LT_LANG(FC)], - [Windows Resource], [_LT_LANG(RC)], - [m4_ifdef([_LT_LANG_]$1[_CONFIG], - [_LT_LANG($1)], - [m4_fatal([$0: unsupported language: "$1"])])])dnl -])# LT_LANG - - -# _LT_LANG(LANGNAME) -# ------------------ -m4_defun([_LT_LANG], -[m4_ifdef([_LT_LANG_]$1[_enabled], [], - [LT_SUPPORTED_TAG([$1])dnl - m4_append([_LT_TAGS], [$1 ])dnl - m4_define([_LT_LANG_]$1[_enabled], [])dnl - _LT_LANG_$1_CONFIG($1)])dnl -])# _LT_LANG - - -# _LT_LANG_DEFAULT_CONFIG -# ----------------------- -m4_defun([_LT_LANG_DEFAULT_CONFIG], -[AC_PROVIDE_IFELSE([AC_PROG_CXX], - [LT_LANG(CXX)], - [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])]) - -AC_PROVIDE_IFELSE([AC_PROG_F77], - [LT_LANG(F77)], - [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])]) - -AC_PROVIDE_IFELSE([AC_PROG_FC], - [LT_LANG(FC)], - [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])]) - -dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal -dnl pulling things in needlessly. -AC_PROVIDE_IFELSE([AC_PROG_GCJ], - [LT_LANG(GCJ)], - [AC_PROVIDE_IFELSE([A][M_PROG_GCJ], - [LT_LANG(GCJ)], - [AC_PROVIDE_IFELSE([LT_PROG_GCJ], - [LT_LANG(GCJ)], - [m4_ifdef([AC_PROG_GCJ], - [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])]) - m4_ifdef([A][M_PROG_GCJ], - [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])]) - m4_ifdef([LT_PROG_GCJ], - [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])]) - -AC_PROVIDE_IFELSE([LT_PROG_RC], - [LT_LANG(RC)], - [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])]) -])# _LT_LANG_DEFAULT_CONFIG - -# Obsolete macros: -AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)]) -AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)]) -AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)]) -AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)]) -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([AC_LIBTOOL_CXX], []) -dnl AC_DEFUN([AC_LIBTOOL_F77], []) -dnl AC_DEFUN([AC_LIBTOOL_FC], []) -dnl AC_DEFUN([AC_LIBTOOL_GCJ], []) - - -# _LT_TAG_COMPILER -# ---------------- -m4_defun([_LT_TAG_COMPILER], -[AC_REQUIRE([AC_PROG_CC])dnl - -_LT_DECL([LTCC], [CC], [1], [A C compiler])dnl -_LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl -_LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl -_LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl - -# If no C compiler was specified, use CC. -LTCC=${LTCC-"$CC"} - -# If no C compiler flags were specified, use CFLAGS. -LTCFLAGS=${LTCFLAGS-"$CFLAGS"} - -# Allow CC to be a program name with arguments. -compiler=$CC -])# _LT_TAG_COMPILER - - -# _LT_COMPILER_BOILERPLATE -# ------------------------ -# Check for compiler boilerplate output or warnings with -# the simple compiler test code. -m4_defun([_LT_COMPILER_BOILERPLATE], -[m4_require([_LT_DECL_SED])dnl -ac_outfile=conftest.$ac_objext -echo "$lt_simple_compile_test_code" >conftest.$ac_ext -eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_compiler_boilerplate=`cat conftest.err` -$RM conftest* -])# _LT_COMPILER_BOILERPLATE - - -# _LT_LINKER_BOILERPLATE -# ---------------------- -# Check for linker boilerplate output or warnings with -# the simple link test code. -m4_defun([_LT_LINKER_BOILERPLATE], -[m4_require([_LT_DECL_SED])dnl -ac_outfile=conftest.$ac_objext -echo "$lt_simple_link_test_code" >conftest.$ac_ext -eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_linker_boilerplate=`cat conftest.err` -$RM -r conftest* -])# _LT_LINKER_BOILERPLATE - -# _LT_REQUIRED_DARWIN_CHECKS -# ------------------------- -m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[ - case $host_os in - rhapsody* | darwin*) - AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:]) - AC_CHECK_TOOL([NMEDIT], [nmedit], [:]) - AC_CHECK_TOOL([LIPO], [lipo], [:]) - AC_CHECK_TOOL([OTOOL], [otool], [:]) - AC_CHECK_TOOL([OTOOL64], [otool64], [:]) - _LT_DECL([], [DSYMUTIL], [1], - [Tool to manipulate archived DWARF debug symbol files on Mac OS X]) - _LT_DECL([], [NMEDIT], [1], - [Tool to change global to local symbols on Mac OS X]) - _LT_DECL([], [LIPO], [1], - [Tool to manipulate fat objects and archives on Mac OS X]) - _LT_DECL([], [OTOOL], [1], - [ldd/readelf like tool for Mach-O binaries on Mac OS X]) - _LT_DECL([], [OTOOL64], [1], - [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4]) - - AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod], - [lt_cv_apple_cc_single_mod=no - if test -z "${LT_MULTI_MODULE}"; then - # By default we will add the -single_module flag. You can override - # by either setting the environment variable LT_MULTI_MODULE - # non-empty at configure time, or by adding -multi_module to the - # link flags. - rm -rf libconftest.dylib* - echo "int foo(void){return 1;}" > conftest.c - echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ --dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD - $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ - -dynamiclib -Wl,-single_module conftest.c 2>conftest.err - _lt_result=$? - if test -f libconftest.dylib && test ! -s conftest.err && test $_lt_result = 0; then - lt_cv_apple_cc_single_mod=yes - else - cat conftest.err >&AS_MESSAGE_LOG_FD - fi - rm -rf libconftest.dylib* - rm -f conftest.* - fi]) - AC_CACHE_CHECK([for -exported_symbols_list linker flag], - [lt_cv_ld_exported_symbols_list], - [lt_cv_ld_exported_symbols_list=no - save_LDFLAGS=$LDFLAGS - echo "_main" > conftest.sym - LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" - AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], - [lt_cv_ld_exported_symbols_list=yes], - [lt_cv_ld_exported_symbols_list=no]) - LDFLAGS="$save_LDFLAGS" - ]) - case $host_os in - rhapsody* | darwin1.[[012]]) - _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;; - darwin1.*) - _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; - darwin*) # darwin 5.x on - # if running on 10.5 or later, the deployment target defaults - # to the OS version, if on x86, and 10.4, the deployment - # target defaults to 10.4. Don't you love it? - case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in - 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*) - _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; - 10.[[012]]*) - _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; - 10.*) - _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; - esac - ;; - esac - if test "$lt_cv_apple_cc_single_mod" = "yes"; then - _lt_dar_single_mod='$single_module' - fi - if test "$lt_cv_ld_exported_symbols_list" = "yes"; then - _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym' - else - _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}' - fi - if test "$DSYMUTIL" != ":"; then - _lt_dsymutil='~$DSYMUTIL $lib || :' - else - _lt_dsymutil= - fi - ;; - esac -]) - - -# _LT_DARWIN_LINKER_FEATURES -# -------------------------- -# Checks for linker and compiler features on darwin -m4_defun([_LT_DARWIN_LINKER_FEATURES], -[ - m4_require([_LT_REQUIRED_DARWIN_CHECKS]) - _LT_TAGVAR(archive_cmds_need_lc, $1)=no - _LT_TAGVAR(hardcode_direct, $1)=no - _LT_TAGVAR(hardcode_automatic, $1)=yes - _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported - _LT_TAGVAR(whole_archive_flag_spec, $1)='' - _LT_TAGVAR(link_all_deplibs, $1)=yes - _LT_TAGVAR(allow_undefined_flag, $1)="$_lt_dar_allow_undefined" - case $cc_basename in - ifort*) _lt_dar_can_shared=yes ;; - *) _lt_dar_can_shared=$GCC ;; - esac - if test "$_lt_dar_can_shared" = "yes"; then - output_verbose_link_cmd=echo - _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" - _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" - _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" - _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" - m4_if([$1], [CXX], -[ if test "$lt_cv_apple_cc_single_mod" != "yes"; then - _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" - _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" - fi -],[]) - else - _LT_TAGVAR(ld_shlibs, $1)=no - fi -]) - -# _LT_SYS_MODULE_PATH_AIX -# ----------------------- -# Links a minimal program and checks the executable -# for the system default hardcoded library path. In most cases, -# this is /usr/lib:/lib, but when the MPI compilers are used -# the location of the communication and MPI libs are included too. -# If we don't find anything, use the default library path according -# to the aix ld manual. -m4_defun([_LT_SYS_MODULE_PATH_AIX], -[m4_require([_LT_DECL_SED])dnl -AC_LINK_IFELSE(AC_LANG_PROGRAM,[ -lt_aix_libpath_sed=' - /Import File Strings/,/^$/ { - /^0/ { - s/^0 *\(.*\)$/\1/ - p - } - }' -aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` -# Check for a 64-bit object if we didn't find anything. -if test -z "$aix_libpath"; then - aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` -fi],[]) -if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi -])# _LT_SYS_MODULE_PATH_AIX - - -# _LT_SHELL_INIT(ARG) -# ------------------- -m4_define([_LT_SHELL_INIT], -[ifdef([AC_DIVERSION_NOTICE], - [AC_DIVERT_PUSH(AC_DIVERSION_NOTICE)], - [AC_DIVERT_PUSH(NOTICE)]) -$1 -AC_DIVERT_POP -])# _LT_SHELL_INIT - - -# _LT_PROG_ECHO_BACKSLASH -# ----------------------- -# Add some code to the start of the generated configure script which -# will find an echo command which doesn't interpret backslashes. -m4_defun([_LT_PROG_ECHO_BACKSLASH], -[_LT_SHELL_INIT([ -# Check that we are running under the correct shell. -SHELL=${CONFIG_SHELL-/bin/sh} - -case X$lt_ECHO in -X*--fallback-echo) - # Remove one level of quotation (which was required for Make). - ECHO=`echo "$lt_ECHO" | sed 's,\\\\\[$]\\[$]0,'[$]0','` - ;; -esac - -ECHO=${lt_ECHO-echo} -if test "X[$]1" = X--no-reexec; then - # Discard the --no-reexec flag, and continue. - shift -elif test "X[$]1" = X--fallback-echo; then - # Avoid inline document here, it may be left over - : -elif test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' ; then - # Yippee, $ECHO works! - : -else - # Restart under the correct shell. - exec $SHELL "[$]0" --no-reexec ${1+"[$]@"} -fi - -if test "X[$]1" = X--fallback-echo; then - # used as fallback echo - shift - cat <<_LT_EOF -[$]* -_LT_EOF - exit 0 -fi - -# The HP-UX ksh and POSIX shell print the target directory to stdout -# if CDPATH is set. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - -if test -z "$lt_ECHO"; then - if test "X${echo_test_string+set}" != Xset; then - # find a string as large as possible, as long as the shell can cope with it - for cmd in 'sed 50q "[$]0"' 'sed 20q "[$]0"' 'sed 10q "[$]0"' 'sed 2q "[$]0"' 'echo test'; do - # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ... - if { echo_test_string=`eval $cmd`; } 2>/dev/null && - { test "X$echo_test_string" = "X$echo_test_string"; } 2>/dev/null - then - break - fi - done - fi - - if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' && - echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` && - test "X$echo_testing_string" = "X$echo_test_string"; then - : - else - # The Solaris, AIX, and Digital Unix default echo programs unquote - # backslashes. This makes it impossible to quote backslashes using - # echo "$something" | sed 's/\\/\\\\/g' - # - # So, first we look for a working echo in the user's PATH. - - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR - for dir in $PATH /usr/ucb; do - IFS="$lt_save_ifs" - if (test -f $dir/echo || test -f $dir/echo$ac_exeext) && - test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' && - echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` && - test "X$echo_testing_string" = "X$echo_test_string"; then - ECHO="$dir/echo" - break - fi - done - IFS="$lt_save_ifs" - - if test "X$ECHO" = Xecho; then - # We didn't find a better echo, so look for alternatives. - if test "X`{ print -r '\t'; } 2>/dev/null`" = 'X\t' && - echo_testing_string=`{ print -r "$echo_test_string"; } 2>/dev/null` && - test "X$echo_testing_string" = "X$echo_test_string"; then - # This shell has a builtin print -r that does the trick. - ECHO='print -r' - elif { test -f /bin/ksh || test -f /bin/ksh$ac_exeext; } && - test "X$CONFIG_SHELL" != X/bin/ksh; then - # If we have ksh, try running configure again with it. - ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh} - export ORIGINAL_CONFIG_SHELL - CONFIG_SHELL=/bin/ksh - export CONFIG_SHELL - exec $CONFIG_SHELL "[$]0" --no-reexec ${1+"[$]@"} - else - # Try using printf. - ECHO='printf %s\n' - if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' && - echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` && - test "X$echo_testing_string" = "X$echo_test_string"; then - # Cool, printf works - : - elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` && - test "X$echo_testing_string" = 'X\t' && - echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` && - test "X$echo_testing_string" = "X$echo_test_string"; then - CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL - export CONFIG_SHELL - SHELL="$CONFIG_SHELL" - export SHELL - ECHO="$CONFIG_SHELL [$]0 --fallback-echo" - elif echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` && - test "X$echo_testing_string" = 'X\t' && - echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` && - test "X$echo_testing_string" = "X$echo_test_string"; then - ECHO="$CONFIG_SHELL [$]0 --fallback-echo" - else - # maybe with a smaller string... - prev=: - - for cmd in 'echo test' 'sed 2q "[$]0"' 'sed 10q "[$]0"' 'sed 20q "[$]0"' 'sed 50q "[$]0"'; do - if { test "X$echo_test_string" = "X`eval $cmd`"; } 2>/dev/null - then - break - fi - prev="$cmd" - done - - if test "$prev" != 'sed 50q "[$]0"'; then - echo_test_string=`eval $prev` - export echo_test_string - exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "[$]0" ${1+"[$]@"} - else - # Oops. We lost completely, so just stick with echo. - ECHO=echo - fi - fi - fi - fi - fi -fi - -# Copy echo and quote the copy suitably for passing to libtool from -# the Makefile, instead of quoting the original, which is used later. -lt_ECHO=$ECHO -if test "X$lt_ECHO" = "X$CONFIG_SHELL [$]0 --fallback-echo"; then - lt_ECHO="$CONFIG_SHELL \\\$\[$]0 --fallback-echo" -fi - -AC_SUBST(lt_ECHO) -]) -_LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts]) -_LT_DECL([], [ECHO], [1], - [An echo program that does not interpret backslashes]) -])# _LT_PROG_ECHO_BACKSLASH - - -# _LT_ENABLE_LOCK -# --------------- -m4_defun([_LT_ENABLE_LOCK], -[AC_ARG_ENABLE([libtool-lock], - [AS_HELP_STRING([--disable-libtool-lock], - [avoid locking (might break parallel builds)])]) -test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes - -# Some flags need to be propagated to the compiler or linker for good -# libtool support. -case $host in -ia64-*-hpux*) - # Find out which ABI we are using. - echo 'int i;' > conftest.$ac_ext - if AC_TRY_EVAL(ac_compile); then - case `/usr/bin/file conftest.$ac_objext` in - *ELF-32*) - HPUX_IA64_MODE="32" - ;; - *ELF-64*) - HPUX_IA64_MODE="64" - ;; - esac - fi - rm -rf conftest* - ;; -*-*-irix6*) - # Find out which ABI we are using. - echo '[#]line __oline__ "configure"' > conftest.$ac_ext - if AC_TRY_EVAL(ac_compile); then - if test "$lt_cv_prog_gnu_ld" = yes; then - case `/usr/bin/file conftest.$ac_objext` in - *32-bit*) - LD="${LD-ld} -melf32bsmip" - ;; - *N32*) - LD="${LD-ld} -melf32bmipn32" - ;; - *64-bit*) - LD="${LD-ld} -melf64bmip" - ;; - esac - else - case `/usr/bin/file conftest.$ac_objext` in - *32-bit*) - LD="${LD-ld} -32" - ;; - *N32*) - LD="${LD-ld} -n32" - ;; - *64-bit*) - LD="${LD-ld} -64" - ;; - esac - fi - fi - rm -rf conftest* - ;; - -x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \ -s390*-*linux*|s390*-*tpf*|sparc*-*linux*) - # Find out which ABI we are using. - echo 'int i;' > conftest.$ac_ext - if AC_TRY_EVAL(ac_compile); then - case `/usr/bin/file conftest.o` in - *32-bit*) - case $host in - x86_64-*kfreebsd*-gnu) - LD="${LD-ld} -m elf_i386_fbsd" - ;; - x86_64-*linux*) - LD="${LD-ld} -m elf_i386" - ;; - ppc64-*linux*|powerpc64-*linux*) - LD="${LD-ld} -m elf32ppclinux" - ;; - s390x-*linux*) - LD="${LD-ld} -m elf_s390" - ;; - sparc64-*linux*) - LD="${LD-ld} -m elf32_sparc" - ;; - esac - ;; - *64-bit*) - case $host in - x86_64-*kfreebsd*-gnu) - LD="${LD-ld} -m elf_x86_64_fbsd" - ;; - x86_64-*linux*) - LD="${LD-ld} -m elf_x86_64" - ;; - ppc*-*linux*|powerpc*-*linux*) - LD="${LD-ld} -m elf64ppc" - ;; - s390*-*linux*|s390*-*tpf*) - LD="${LD-ld} -m elf64_s390" - ;; - sparc*-*linux*) - LD="${LD-ld} -m elf64_sparc" - ;; - esac - ;; - esac - fi - rm -rf conftest* - ;; - -*-*-sco3.2v5*) - # On SCO OpenServer 5, we need -belf to get full-featured binaries. - SAVE_CFLAGS="$CFLAGS" - CFLAGS="$CFLAGS -belf" - AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, - [AC_LANG_PUSH(C) - AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) - AC_LANG_POP]) - if test x"$lt_cv_cc_needs_belf" != x"yes"; then - # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf - CFLAGS="$SAVE_CFLAGS" - fi - ;; -sparc*-*solaris*) - # Find out which ABI we are using. - echo 'int i;' > conftest.$ac_ext - if AC_TRY_EVAL(ac_compile); then - case `/usr/bin/file conftest.o` in - *64-bit*) - case $lt_cv_prog_gnu_ld in - yes*) LD="${LD-ld} -m elf64_sparc" ;; - *) - if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then - LD="${LD-ld} -64" - fi - ;; - esac - ;; - esac - fi - rm -rf conftest* - ;; -esac - -need_locks="$enable_libtool_lock" -])# _LT_ENABLE_LOCK - - -# _LT_CMD_OLD_ARCHIVE -# ------------------- -m4_defun([_LT_CMD_OLD_ARCHIVE], -[AC_CHECK_TOOL(AR, ar, false) -test -z "$AR" && AR=ar -test -z "$AR_FLAGS" && AR_FLAGS=cru -_LT_DECL([], [AR], [1], [The archiver]) -_LT_DECL([], [AR_FLAGS], [1]) - -AC_CHECK_TOOL(STRIP, strip, :) -test -z "$STRIP" && STRIP=: -_LT_DECL([], [STRIP], [1], [A symbol stripping program]) - -AC_CHECK_TOOL(RANLIB, ranlib, :) -test -z "$RANLIB" && RANLIB=: -_LT_DECL([], [RANLIB], [1], - [Commands used to install an old-style archive]) - -# Determine commands to create old-style static archives. -old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' -old_postinstall_cmds='chmod 644 $oldlib' -old_postuninstall_cmds= - -if test -n "$RANLIB"; then - case $host_os in - openbsd*) - old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$oldlib" - ;; - *) - old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$oldlib" - ;; - esac - old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib" -fi -_LT_DECL([], [old_postinstall_cmds], [2]) -_LT_DECL([], [old_postuninstall_cmds], [2]) -_LT_TAGDECL([], [old_archive_cmds], [2], - [Commands used to build an old-style archive]) -])# _LT_CMD_OLD_ARCHIVE - - -# _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, -# [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) -# ---------------------------------------------------------------- -# Check whether the given compiler option works -AC_DEFUN([_LT_COMPILER_OPTION], -[m4_require([_LT_FILEUTILS_DEFAULTS])dnl -m4_require([_LT_DECL_SED])dnl -AC_CACHE_CHECK([$1], [$2], - [$2=no - m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) - echo "$lt_simple_compile_test_code" > conftest.$ac_ext - lt_compiler_flag="$3" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - # The option is referenced via a variable to avoid confusing sed. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD) - (eval "$lt_compile" 2>conftest.err) - ac_status=$? - cat conftest.err >&AS_MESSAGE_LOG_FD - echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD - if (exit $ac_status) && test -s "$ac_outfile"; then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings other than the usual output. - $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then - $2=yes - fi - fi - $RM conftest* -]) - -if test x"[$]$2" = xyes; then - m4_if([$5], , :, [$5]) -else - m4_if([$6], , :, [$6]) -fi -])# _LT_COMPILER_OPTION - -# Old name: -AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION]) -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], []) - - -# _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, -# [ACTION-SUCCESS], [ACTION-FAILURE]) -# ---------------------------------------------------- -# Check whether the given linker option works -AC_DEFUN([_LT_LINKER_OPTION], -[m4_require([_LT_FILEUTILS_DEFAULTS])dnl -m4_require([_LT_DECL_SED])dnl -AC_CACHE_CHECK([$1], [$2], - [$2=no - save_LDFLAGS="$LDFLAGS" - LDFLAGS="$LDFLAGS $3" - echo "$lt_simple_link_test_code" > conftest.$ac_ext - if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then - # The linker can only warn and ignore the option if not recognized - # So say no if there are warnings - if test -s conftest.err; then - # Append any errors to the config.log. - cat conftest.err 1>&AS_MESSAGE_LOG_FD - $ECHO "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if diff conftest.exp conftest.er2 >/dev/null; then - $2=yes - fi - else - $2=yes - fi - fi - $RM -r conftest* - LDFLAGS="$save_LDFLAGS" -]) - -if test x"[$]$2" = xyes; then - m4_if([$4], , :, [$4]) -else - m4_if([$5], , :, [$5]) -fi -])# _LT_LINKER_OPTION - -# Old name: -AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION]) -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], []) - - -# LT_CMD_MAX_LEN -#--------------- -AC_DEFUN([LT_CMD_MAX_LEN], -[AC_REQUIRE([AC_CANONICAL_HOST])dnl -# find the maximum length of command line arguments -AC_MSG_CHECKING([the maximum length of command line arguments]) -AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl - i=0 - teststring="ABCD" - - case $build_os in - msdosdjgpp*) - # On DJGPP, this test can blow up pretty badly due to problems in libc - # (any single argument exceeding 2000 bytes causes a buffer overrun - # during glob expansion). Even if it were fixed, the result of this - # check would be larger than it should be. - lt_cv_sys_max_cmd_len=12288; # 12K is about right - ;; - - gnu*) - # Under GNU Hurd, this test is not required because there is - # no limit to the length of command line arguments. - # Libtool will interpret -1 as no limit whatsoever - lt_cv_sys_max_cmd_len=-1; - ;; - - cygwin* | mingw* | cegcc*) - # On Win9x/ME, this test blows up -- it succeeds, but takes - # about 5 minutes as the teststring grows exponentially. - # Worse, since 9x/ME are not pre-emptively multitasking, - # you end up with a "frozen" computer, even though with patience - # the test eventually succeeds (with a max line length of 256k). - # Instead, let's just punt: use the minimum linelength reported by - # all of the supported platforms: 8192 (on NT/2K/XP). - lt_cv_sys_max_cmd_len=8192; - ;; - - amigaos*) - # On AmigaOS with pdksh, this test takes hours, literally. - # So we just punt and use a minimum line length of 8192. - lt_cv_sys_max_cmd_len=8192; - ;; - - netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) - # This has been around since 386BSD, at least. Likely further. - if test -x /sbin/sysctl; then - lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` - elif test -x /usr/sbin/sysctl; then - lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` - else - lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs - fi - # And add a safety zone - lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` - lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` - ;; - - interix*) - # We know the value 262144 and hardcode it with a safety zone (like BSD) - lt_cv_sys_max_cmd_len=196608 - ;; - - osf*) - # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure - # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not - # nice to cause kernel panics so lets avoid the loop below. - # First set a reasonable default. - lt_cv_sys_max_cmd_len=16384 - # - if test -x /sbin/sysconfig; then - case `/sbin/sysconfig -q proc exec_disable_arg_limit` in - *1*) lt_cv_sys_max_cmd_len=-1 ;; - esac - fi - ;; - sco3.2v5*) - lt_cv_sys_max_cmd_len=102400 - ;; - sysv5* | sco5v6* | sysv4.2uw2*) - kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` - if test -n "$kargmax"; then - lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'` - else - lt_cv_sys_max_cmd_len=32768 - fi - ;; - *) - lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` - if test -n "$lt_cv_sys_max_cmd_len"; then - lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` - lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` - else - # Make teststring a little bigger before we do anything with it. - # a 1K string should be a reasonable start. - for i in 1 2 3 4 5 6 7 8 ; do - teststring=$teststring$teststring - done - SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} - # If test is not a shell built-in, we'll probably end up computing a - # maximum length that is only half of the actual maximum length, but - # we can't tell. - while { test "X"`$SHELL [$]0 --fallback-echo "X$teststring$teststring" 2>/dev/null` \ - = "XX$teststring$teststring"; } >/dev/null 2>&1 && - test $i != 17 # 1/2 MB should be enough - do - i=`expr $i + 1` - teststring=$teststring$teststring - done - # Only check the string length outside the loop. - lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` - teststring= - # Add a significant safety factor because C++ compilers can tack on - # massive amounts of additional arguments before passing them to the - # linker. It appears as though 1/2 is a usable value. - lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` - fi - ;; - esac -]) -if test -n $lt_cv_sys_max_cmd_len ; then - AC_MSG_RESULT($lt_cv_sys_max_cmd_len) -else - AC_MSG_RESULT(none) -fi -max_cmd_len=$lt_cv_sys_max_cmd_len -_LT_DECL([], [max_cmd_len], [0], - [What is the maximum length of a command?]) -])# LT_CMD_MAX_LEN - -# Old name: -AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN]) -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], []) - - -# _LT_HEADER_DLFCN -# ---------------- -m4_defun([_LT_HEADER_DLFCN], -[AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl -])# _LT_HEADER_DLFCN - - -# _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, -# ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) -# ---------------------------------------------------------------- -m4_defun([_LT_TRY_DLOPEN_SELF], -[m4_require([_LT_HEADER_DLFCN])dnl -if test "$cross_compiling" = yes; then : - [$4] -else - lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 - lt_status=$lt_dlunknown - cat > conftest.$ac_ext <<_LT_EOF -[#line __oline__ "configure" -#include "confdefs.h" - -#if HAVE_DLFCN_H -#include -#endif - -#include - -#ifdef RTLD_GLOBAL -# define LT_DLGLOBAL RTLD_GLOBAL -#else -# ifdef DL_GLOBAL -# define LT_DLGLOBAL DL_GLOBAL -# else -# define LT_DLGLOBAL 0 -# endif -#endif - -/* We may have to define LT_DLLAZY_OR_NOW in the command line if we - find out it does not work in some platform. */ -#ifndef LT_DLLAZY_OR_NOW -# ifdef RTLD_LAZY -# define LT_DLLAZY_OR_NOW RTLD_LAZY -# else -# ifdef DL_LAZY -# define LT_DLLAZY_OR_NOW DL_LAZY -# else -# ifdef RTLD_NOW -# define LT_DLLAZY_OR_NOW RTLD_NOW -# else -# ifdef DL_NOW -# define LT_DLLAZY_OR_NOW DL_NOW -# else -# define LT_DLLAZY_OR_NOW 0 -# endif -# endif -# endif -# endif -#endif - -void fnord() { int i=42;} -int main () -{ - void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); - int status = $lt_dlunknown; - - if (self) - { - if (dlsym (self,"fnord")) status = $lt_dlno_uscore; - else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; - /* dlclose (self); */ - } - else - puts (dlerror ()); - - return status; -}] -_LT_EOF - if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then - (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null - lt_status=$? - case x$lt_status in - x$lt_dlno_uscore) $1 ;; - x$lt_dlneed_uscore) $2 ;; - x$lt_dlunknown|x*) $3 ;; - esac - else : - # compilation failed - $3 - fi -fi -rm -fr conftest* -])# _LT_TRY_DLOPEN_SELF - - -# LT_SYS_DLOPEN_SELF -# ------------------ -AC_DEFUN([LT_SYS_DLOPEN_SELF], -[m4_require([_LT_HEADER_DLFCN])dnl -if test "x$enable_dlopen" != xyes; then - enable_dlopen=unknown - enable_dlopen_self=unknown - enable_dlopen_self_static=unknown -else - lt_cv_dlopen=no - lt_cv_dlopen_libs= - - case $host_os in - beos*) - lt_cv_dlopen="load_add_on" - lt_cv_dlopen_libs= - lt_cv_dlopen_self=yes - ;; - - mingw* | pw32* | cegcc*) - lt_cv_dlopen="LoadLibrary" - lt_cv_dlopen_libs= - ;; - - cygwin*) - lt_cv_dlopen="dlopen" - lt_cv_dlopen_libs= - ;; - - darwin*) - # if libdl is installed we need to link against it - AC_CHECK_LIB([dl], [dlopen], - [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[ - lt_cv_dlopen="dyld" - lt_cv_dlopen_libs= - lt_cv_dlopen_self=yes - ]) - ;; - - *) - AC_CHECK_FUNC([shl_load], - [lt_cv_dlopen="shl_load"], - [AC_CHECK_LIB([dld], [shl_load], - [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"], - [AC_CHECK_FUNC([dlopen], - [lt_cv_dlopen="dlopen"], - [AC_CHECK_LIB([dl], [dlopen], - [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"], - [AC_CHECK_LIB([svld], [dlopen], - [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"], - [AC_CHECK_LIB([dld], [dld_link], - [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"]) - ]) - ]) - ]) - ]) - ]) - ;; - esac - - if test "x$lt_cv_dlopen" != xno; then - enable_dlopen=yes - else - enable_dlopen=no - fi - - case $lt_cv_dlopen in - dlopen) - save_CPPFLAGS="$CPPFLAGS" - test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" - - save_LDFLAGS="$LDFLAGS" - wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" - - save_LIBS="$LIBS" - LIBS="$lt_cv_dlopen_libs $LIBS" - - AC_CACHE_CHECK([whether a program can dlopen itself], - lt_cv_dlopen_self, [dnl - _LT_TRY_DLOPEN_SELF( - lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes, - lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) - ]) - - if test "x$lt_cv_dlopen_self" = xyes; then - wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" - AC_CACHE_CHECK([whether a statically linked program can dlopen itself], - lt_cv_dlopen_self_static, [dnl - _LT_TRY_DLOPEN_SELF( - lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes, - lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross) - ]) - fi - - CPPFLAGS="$save_CPPFLAGS" - LDFLAGS="$save_LDFLAGS" - LIBS="$save_LIBS" - ;; - esac - - case $lt_cv_dlopen_self in - yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; - *) enable_dlopen_self=unknown ;; - esac - - case $lt_cv_dlopen_self_static in - yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; - *) enable_dlopen_self_static=unknown ;; - esac -fi -_LT_DECL([dlopen_support], [enable_dlopen], [0], - [Whether dlopen is supported]) -_LT_DECL([dlopen_self], [enable_dlopen_self], [0], - [Whether dlopen of programs is supported]) -_LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0], - [Whether dlopen of statically linked programs is supported]) -])# LT_SYS_DLOPEN_SELF - -# Old name: -AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF]) -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], []) - - -# _LT_COMPILER_C_O([TAGNAME]) -# --------------------------- -# Check to see if options -c and -o are simultaneously supported by compiler. -# This macro does not hard code the compiler like AC_PROG_CC_C_O. -m4_defun([_LT_COMPILER_C_O], -[m4_require([_LT_DECL_SED])dnl -m4_require([_LT_FILEUTILS_DEFAULTS])dnl -m4_require([_LT_TAG_COMPILER])dnl -AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext], - [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)], - [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no - $RM -r conftest 2>/dev/null - mkdir conftest - cd conftest - mkdir out - echo "$lt_simple_compile_test_code" > conftest.$ac_ext - - lt_compiler_flag="-o out/conftest2.$ac_objext" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD) - (eval "$lt_compile" 2>out/conftest.err) - ac_status=$? - cat out/conftest.err >&AS_MESSAGE_LOG_FD - echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD - if (exit $ac_status) && test -s out/conftest2.$ac_objext - then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings - $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp - $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 - if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then - _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes - fi - fi - chmod u+w . 2>&AS_MESSAGE_LOG_FD - $RM conftest* - # SGI C++ compiler will create directory out/ii_files/ for - # template instantiation - test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files - $RM out/* && rmdir out - cd .. - $RM -r conftest - $RM conftest* -]) -_LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1], - [Does compiler simultaneously support -c and -o options?]) -])# _LT_COMPILER_C_O - - -# _LT_COMPILER_FILE_LOCKS([TAGNAME]) -# ---------------------------------- -# Check to see if we can do hard links to lock some files if needed -m4_defun([_LT_COMPILER_FILE_LOCKS], -[m4_require([_LT_ENABLE_LOCK])dnl -m4_require([_LT_FILEUTILS_DEFAULTS])dnl -_LT_COMPILER_C_O([$1]) - -hard_links="nottested" -if test "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" != no; then - # do not overwrite the value of need_locks provided by the user - AC_MSG_CHECKING([if we can lock with hard links]) - hard_links=yes - $RM conftest* - ln conftest.a conftest.b 2>/dev/null && hard_links=no - touch conftest.a - ln conftest.a conftest.b 2>&5 || hard_links=no - ln conftest.a conftest.b 2>/dev/null && hard_links=no - AC_MSG_RESULT([$hard_links]) - if test "$hard_links" = no; then - AC_MSG_WARN([`$CC' does not support `-c -o', so `make -j' may be unsafe]) - need_locks=warn - fi -else - need_locks=no -fi -_LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?]) -])# _LT_COMPILER_FILE_LOCKS - - -# _LT_CHECK_OBJDIR -# ---------------- -m4_defun([_LT_CHECK_OBJDIR], -[AC_CACHE_CHECK([for objdir], [lt_cv_objdir], -[rm -f .libs 2>/dev/null -mkdir .libs 2>/dev/null -if test -d .libs; then - lt_cv_objdir=.libs -else - # MS-DOS does not allow filenames that begin with a dot. - lt_cv_objdir=_libs -fi -rmdir .libs 2>/dev/null]) -objdir=$lt_cv_objdir -_LT_DECL([], [objdir], [0], - [The name of the directory that contains temporary libtool files])dnl -m4_pattern_allow([LT_OBJDIR])dnl -AC_DEFINE_UNQUOTED(LT_OBJDIR, "$lt_cv_objdir/", - [Define to the sub-directory in which libtool stores uninstalled libraries.]) -])# _LT_CHECK_OBJDIR - - -# _LT_LINKER_HARDCODE_LIBPATH([TAGNAME]) -# -------------------------------------- -# Check hardcoding attributes. -m4_defun([_LT_LINKER_HARDCODE_LIBPATH], -[AC_MSG_CHECKING([how to hardcode library paths into programs]) -_LT_TAGVAR(hardcode_action, $1)= -if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" || - test -n "$_LT_TAGVAR(runpath_var, $1)" || - test "X$_LT_TAGVAR(hardcode_automatic, $1)" = "Xyes" ; then - - # We can hardcode non-existent directories. - if test "$_LT_TAGVAR(hardcode_direct, $1)" != no && - # If the only mechanism to avoid hardcoding is shlibpath_var, we - # have to relink, otherwise we might link with an installed library - # when we should be linking with a yet-to-be-installed one - ## test "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" != no && - test "$_LT_TAGVAR(hardcode_minus_L, $1)" != no; then - # Linking always hardcodes the temporary library directory. - _LT_TAGVAR(hardcode_action, $1)=relink - else - # We can link without hardcoding, and we can hardcode nonexisting dirs. - _LT_TAGVAR(hardcode_action, $1)=immediate - fi -else - # We cannot hardcode anything, or else we can only hardcode existing - # directories. - _LT_TAGVAR(hardcode_action, $1)=unsupported -fi -AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)]) - -if test "$_LT_TAGVAR(hardcode_action, $1)" = relink || - test "$_LT_TAGVAR(inherit_rpath, $1)" = yes; then - # Fast installation is not supported - enable_fast_install=no -elif test "$shlibpath_overrides_runpath" = yes || - test "$enable_shared" = no; then - # Fast installation is not necessary - enable_fast_install=needless -fi -_LT_TAGDECL([], [hardcode_action], [0], - [How to hardcode a shared library path into an executable]) -])# _LT_LINKER_HARDCODE_LIBPATH - - -# _LT_CMD_STRIPLIB -# ---------------- -m4_defun([_LT_CMD_STRIPLIB], -[m4_require([_LT_DECL_EGREP]) -striplib= -old_striplib= -AC_MSG_CHECKING([whether stripping libraries is possible]) -if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then - test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" - test -z "$striplib" && striplib="$STRIP --strip-unneeded" - AC_MSG_RESULT([yes]) -else -# FIXME - insert some real tests, host_os isn't really good enough - case $host_os in - darwin*) - if test -n "$STRIP" ; then - striplib="$STRIP -x" - old_striplib="$STRIP -S" - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) - fi - ;; - *) - AC_MSG_RESULT([no]) - ;; - esac -fi -_LT_DECL([], [old_striplib], [1], [Commands to strip libraries]) -_LT_DECL([], [striplib], [1]) -])# _LT_CMD_STRIPLIB - - -# _LT_SYS_DYNAMIC_LINKER([TAG]) -# ----------------------------- -# PORTME Fill in your ld.so characteristics -m4_defun([_LT_SYS_DYNAMIC_LINKER], -[AC_REQUIRE([AC_CANONICAL_HOST])dnl -m4_require([_LT_DECL_EGREP])dnl -m4_require([_LT_FILEUTILS_DEFAULTS])dnl -m4_require([_LT_DECL_OBJDUMP])dnl -m4_require([_LT_DECL_SED])dnl -AC_MSG_CHECKING([dynamic linker characteristics]) -m4_if([$1], - [], [ -if test "$GCC" = yes; then - case $host_os in - darwin*) lt_awk_arg="/^libraries:/,/LR/" ;; - *) lt_awk_arg="/^libraries:/" ;; - esac - lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e "s,=/,/,g"` - if $ECHO "$lt_search_path_spec" | $GREP ';' >/dev/null ; then - # if the path contains ";" then we assume it to be the separator - # otherwise default to the standard path separator (i.e. ":") - it is - # assumed that no part of a normal pathname contains ";" but that should - # okay in the real world where ";" in dirpaths is itself problematic. - lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e 's/;/ /g'` - else - lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` - fi - # Ok, now we have the path, separated by spaces, we can step through it - # and add multilib dir if necessary. - lt_tmp_lt_search_path_spec= - lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` - for lt_sys_path in $lt_search_path_spec; do - if test -d "$lt_sys_path/$lt_multi_os_dir"; then - lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir" - else - test -d "$lt_sys_path" && \ - lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" - fi - done - lt_search_path_spec=`$ECHO $lt_tmp_lt_search_path_spec | awk ' -BEGIN {RS=" "; FS="/|\n";} { - lt_foo=""; - lt_count=0; - for (lt_i = NF; lt_i > 0; lt_i--) { - if ($lt_i != "" && $lt_i != ".") { - if ($lt_i == "..") { - lt_count++; - } else { - if (lt_count == 0) { - lt_foo="/" $lt_i lt_foo; - } else { - lt_count--; - } - } - } - } - if (lt_foo != "") { lt_freq[[lt_foo]]++; } - if (lt_freq[[lt_foo]] == 1) { print lt_foo; } -}'` - sys_lib_search_path_spec=`$ECHO $lt_search_path_spec` -else - sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" -fi]) -library_names_spec= -libname_spec='lib$name' -soname_spec= -shrext_cmds=".so" -postinstall_cmds= -postuninstall_cmds= -finish_cmds= -finish_eval= -shlibpath_var= -shlibpath_overrides_runpath=unknown -version_type=none -dynamic_linker="$host_os ld.so" -sys_lib_dlsearch_path_spec="/lib /usr/lib" -need_lib_prefix=unknown -hardcode_into_libs=no - -# when you set need_version to no, make sure it does not cause -set_version -# flags to be left without arguments -need_version=unknown - -case $host_os in -aix3*) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' - shlibpath_var=LIBPATH - - # AIX 3 has no versioning support, so we append a major version to the name. - soname_spec='${libname}${release}${shared_ext}$major' - ;; - -aix[[4-9]]*) - version_type=linux - need_lib_prefix=no - need_version=no - hardcode_into_libs=yes - if test "$host_cpu" = ia64; then - # AIX 5 supports IA64 - library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - else - # With GCC up to 2.95.x, collect2 would create an import file - # for dependence libraries. The import file would start with - # the line `#! .'. This would cause the generated library to - # depend on `.', always an invalid library. This was fixed in - # development snapshots of GCC prior to 3.0. - case $host_os in - aix4 | aix4.[[01]] | aix4.[[01]].*) - if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' - echo ' yes ' - echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then - : - else - can_build_shared=no - fi - ;; - esac - # AIX (on Power*) has no versioning support, so currently we can not hardcode correct - # soname into executable. Probably we can add versioning support to - # collect2, so additional links can be useful in future. - if test "$aix_use_runtimelinking" = yes; then - # If using run time linking (on AIX 4.2 or later) use lib.so - # instead of lib.a to let people know that these are not - # typical AIX shared libraries. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - else - # We preserve .a as extension for shared libraries through AIX4.2 - # and later when we are not doing run time linking. - library_names_spec='${libname}${release}.a $libname.a' - soname_spec='${libname}${release}${shared_ext}$major' - fi - shlibpath_var=LIBPATH - fi - ;; - -amigaos*) - case $host_cpu in - powerpc) - # Since July 2007 AmigaOS4 officially supports .so libraries. - # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - ;; - m68k) - library_names_spec='$libname.ixlibrary $libname.a' - # Create ${libname}_ixlibrary.a entries in /sys/libs. - finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$ECHO "X$lib" | $Xsed -e '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' - ;; - esac - ;; - -beos*) - library_names_spec='${libname}${shared_ext}' - dynamic_linker="$host_os ld.so" - shlibpath_var=LIBRARY_PATH - ;; - -bsdi[[45]]*) - version_type=linux - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' - shlibpath_var=LD_LIBRARY_PATH - sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" - sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" - # the default ld.so.conf also contains /usr/contrib/lib and - # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow - # libtool to hard-code these into programs - ;; - -cygwin* | mingw* | pw32* | cegcc*) - version_type=windows - shrext_cmds=".dll" - need_version=no - need_lib_prefix=no - - case $GCC,$host_os in - yes,cygwin* | yes,mingw* | yes,pw32* | yes,cegcc*) - library_names_spec='$libname.dll.a' - # DLL is installed to $(libdir)/../bin by postinstall_cmds - postinstall_cmds='base_file=`basename \${file}`~ - dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ - dldir=$destdir/`dirname \$dlpath`~ - test -d \$dldir || mkdir -p \$dldir~ - $install_prog $dir/$dlname \$dldir/$dlname~ - chmod a+x \$dldir/$dlname~ - if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then - eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; - fi' - postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ - dlpath=$dir/\$dldll~ - $RM \$dlpath' - shlibpath_overrides_runpath=yes - - case $host_os in - cygwin*) - # Cygwin DLLs use 'cyg' prefix rather than 'lib' - soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' - sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib" - ;; - mingw* | cegcc*) - # MinGW DLLs use traditional 'lib' prefix - soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' - sys_lib_search_path_spec=`$CC -print-search-dirs | $GREP "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` - if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then - # It is most probably a Windows format PATH printed by - # mingw gcc, but we are running on Cygwin. Gcc prints its search - # path with ; separators, and with drive letters. We can handle the - # drive letters (cygwin fileutils understands them), so leave them, - # especially as we might pass files found there to a mingw objdump, - # which wouldn't understand a cygwinified path. Ahh. - sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` - else - sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` - fi - ;; - pw32*) - # pw32 DLLs use 'pw' prefix rather than 'lib' - library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' - ;; - esac - ;; - - *) - library_names_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext} $libname.lib' - ;; - esac - dynamic_linker='Win32 ld.exe' - # FIXME: first we should search . and the directory the executable is in - shlibpath_var=PATH - ;; - -darwin* | rhapsody*) - dynamic_linker="$host_os dyld" - version_type=darwin - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' - soname_spec='${libname}${release}${major}$shared_ext' - shlibpath_overrides_runpath=yes - shlibpath_var=DYLD_LIBRARY_PATH - shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' -m4_if([$1], [],[ - sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"]) - sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' - ;; - -dgux*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - ;; - -freebsd1*) - dynamic_linker=no - ;; - -freebsd* | dragonfly*) - # DragonFly does not have aout. When/if they implement a new - # versioning mechanism, adjust this. - if test -x /usr/bin/objformat; then - objformat=`/usr/bin/objformat` - else - case $host_os in - freebsd[[123]]*) objformat=aout ;; - *) objformat=elf ;; - esac - fi - version_type=freebsd-$objformat - case $version_type in - freebsd-elf*) - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' - need_version=no - need_lib_prefix=no - ;; - freebsd-*) - library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' - need_version=yes - ;; - esac - shlibpath_var=LD_LIBRARY_PATH - case $host_os in - freebsd2*) - shlibpath_overrides_runpath=yes - ;; - freebsd3.[[01]]* | freebsdelf3.[[01]]*) - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \ - freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1) - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; - *) # from 4.6 on, and DragonFly - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - esac - ;; - -gnu*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - hardcode_into_libs=yes - ;; - -hpux9* | hpux10* | hpux11*) - # Give a soname corresponding to the major version so that dld.sl refuses to - # link against other versions. - version_type=sunos - need_lib_prefix=no - need_version=no - case $host_cpu in - ia64*) - shrext_cmds='.so' - hardcode_into_libs=yes - dynamic_linker="$host_os dld.so" - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - if test "X$HPUX_IA64_MODE" = X32; then - sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" - else - sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" - fi - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec - ;; - hppa*64*) - shrext_cmds='.sl' - hardcode_into_libs=yes - dynamic_linker="$host_os dld.sl" - shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH - shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec - ;; - *) - shrext_cmds='.sl' - dynamic_linker="$host_os dld.sl" - shlibpath_var=SHLIB_PATH - shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - ;; - esac - # HP-UX runs *really* slowly unless shared libraries are mode 555. - postinstall_cmds='chmod 555 $lib' - ;; - -interix[[3-9]]*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; - -irix5* | irix6* | nonstopux*) - case $host_os in - nonstopux*) version_type=nonstopux ;; - *) - if test "$lt_cv_prog_gnu_ld" = yes; then - version_type=linux - else - version_type=irix - fi ;; - esac - need_lib_prefix=no - need_version=no - soname_spec='${libname}${release}${shared_ext}$major' - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' - case $host_os in - irix5* | nonstopux*) - libsuff= shlibsuff= - ;; - *) - case $LD in # libtool.m4 will add one of these switches to LD - *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") - libsuff= shlibsuff= libmagic=32-bit;; - *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") - libsuff=32 shlibsuff=N32 libmagic=N32;; - *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") - libsuff=64 shlibsuff=64 libmagic=64-bit;; - *) libsuff= shlibsuff= libmagic=never-match;; - esac - ;; - esac - shlibpath_var=LD_LIBRARY${shlibsuff}_PATH - shlibpath_overrides_runpath=no - sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" - sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" - hardcode_into_libs=yes - ;; - -# No shared lib support for Linux oldld, aout, or coff. -linux*oldld* | linux*aout* | linux*coff*) - dynamic_linker=no - ;; - -# This must be Linux ELF. -linux* | k*bsd*-gnu | kopensolaris*-gnu) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - # Some binutils ld are patched to set DT_RUNPATH - save_LDFLAGS=$LDFLAGS - save_libdir=$libdir - eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \ - LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\"" - AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], - [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null], - [shlibpath_overrides_runpath=yes])]) - LDFLAGS=$save_LDFLAGS - libdir=$save_libdir - - # This implies no fast_install, which is unacceptable. - # Some rework will be needed to allow for fast_install - # before this can be enabled. - hardcode_into_libs=yes - - # Append ld.so.conf contents to the search path - if test -f /etc/ld.so.conf; then - lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '` - sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" - fi - - # We used to test for /lib/ld.so.1 and disable shared libraries on - # powerpc, because MkLinux only supported shared libraries with the - # GNU dynamic linker. Since this was broken with cross compilers, - # most powerpc-linux boxes support dynamic linking these days and - # people can always --disable-shared, the test was removed, and we - # assume the GNU/Linux dynamic linker is in use. - dynamic_linker='GNU/Linux ld.so' - ;; - -netbsdelf*-gnu) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - dynamic_linker='NetBSD ld.elf_so' - ;; - -netbsd*) - version_type=sunos - need_lib_prefix=no - need_version=no - if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' - dynamic_linker='NetBSD (a.out) ld.so' - else - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - dynamic_linker='NetBSD ld.elf_so' - fi - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - -newsos6) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - ;; - -*nto* | *qnx*) - version_type=qnx - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - dynamic_linker='ldqnx.so' - ;; - -openbsd*) - version_type=sunos - sys_lib_dlsearch_path_spec="/usr/lib" - need_lib_prefix=no - # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. - case $host_os in - openbsd3.3 | openbsd3.3.*) need_version=yes ;; - *) need_version=no ;; - esac - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' - shlibpath_var=LD_LIBRARY_PATH - if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - case $host_os in - openbsd2.[[89]] | openbsd2.[[89]].*) - shlibpath_overrides_runpath=no - ;; - *) - shlibpath_overrides_runpath=yes - ;; - esac - else - shlibpath_overrides_runpath=yes - fi - ;; - -os2*) - libname_spec='$name' - shrext_cmds=".dll" - need_lib_prefix=no - library_names_spec='$libname${shared_ext} $libname.a' - dynamic_linker='OS/2 ld.exe' - shlibpath_var=LIBPATH - ;; - -osf3* | osf4* | osf5*) - version_type=osf - need_lib_prefix=no - need_version=no - soname_spec='${libname}${release}${shared_ext}$major' - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" - sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" - ;; - -rdos*) - dynamic_linker=no - ;; - -solaris*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - # ldd complains unless libraries are executable - postinstall_cmds='chmod +x $lib' - ;; - -sunos4*) - version_type=sunos - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - if test "$with_gnu_ld" = yes; then - need_lib_prefix=no - fi - need_version=yes - ;; - -sysv4 | sysv4.3*) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - case $host_vendor in - sni) - shlibpath_overrides_runpath=no - need_lib_prefix=no - runpath_var=LD_RUN_PATH - ;; - siemens) - need_lib_prefix=no - ;; - motorola) - need_lib_prefix=no - need_version=no - shlibpath_overrides_runpath=no - sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' - ;; - esac - ;; - -sysv4*MP*) - if test -d /usr/nec ;then - version_type=linux - library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' - soname_spec='$libname${shared_ext}.$major' - shlibpath_var=LD_LIBRARY_PATH - fi - ;; - -sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) - version_type=freebsd-elf - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - if test "$with_gnu_ld" = yes; then - sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' - else - sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' - case $host_os in - sco3.2v5*) - sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" - ;; - esac - fi - sys_lib_dlsearch_path_spec='/usr/lib' - ;; - -tpf*) - # TPF is a cross-target only. Preferred cross-host = GNU/Linux. - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; - -uts4*) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - ;; - -*) - dynamic_linker=no - ;; -esac -AC_MSG_RESULT([$dynamic_linker]) -test "$dynamic_linker" = no && can_build_shared=no - -variables_saved_for_relink="PATH $shlibpath_var $runpath_var" -if test "$GCC" = yes; then - variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" -fi - -if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then - sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" -fi -if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then - sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" -fi - -_LT_DECL([], [variables_saved_for_relink], [1], - [Variables whose values should be saved in libtool wrapper scripts and - restored at link time]) -_LT_DECL([], [need_lib_prefix], [0], - [Do we need the "lib" prefix for modules?]) -_LT_DECL([], [need_version], [0], [Do we need a version for libraries?]) -_LT_DECL([], [version_type], [0], [Library versioning type]) -_LT_DECL([], [runpath_var], [0], [Shared library runtime path variable]) -_LT_DECL([], [shlibpath_var], [0],[Shared library path variable]) -_LT_DECL([], [shlibpath_overrides_runpath], [0], - [Is shlibpath searched before the hard-coded library search path?]) -_LT_DECL([], [libname_spec], [1], [Format of library name prefix]) -_LT_DECL([], [library_names_spec], [1], - [[List of archive names. First name is the real one, the rest are links. - The last name is the one that the linker finds with -lNAME]]) -_LT_DECL([], [soname_spec], [1], - [[The coded name of the library, if different from the real name]]) -_LT_DECL([], [postinstall_cmds], [2], - [Command to use after installation of a shared archive]) -_LT_DECL([], [postuninstall_cmds], [2], - [Command to use after uninstallation of a shared archive]) -_LT_DECL([], [finish_cmds], [2], - [Commands used to finish a libtool library installation in a directory]) -_LT_DECL([], [finish_eval], [1], - [[As "finish_cmds", except a single script fragment to be evaled but - not shown]]) -_LT_DECL([], [hardcode_into_libs], [0], - [Whether we should hardcode library paths into libraries]) -_LT_DECL([], [sys_lib_search_path_spec], [2], - [Compile-time system search path for libraries]) -_LT_DECL([], [sys_lib_dlsearch_path_spec], [2], - [Run-time system search path for libraries]) -])# _LT_SYS_DYNAMIC_LINKER - - -# _LT_PATH_TOOL_PREFIX(TOOL) -# -------------------------- -# find a file program which can recognize shared library -AC_DEFUN([_LT_PATH_TOOL_PREFIX], -[m4_require([_LT_DECL_EGREP])dnl -AC_MSG_CHECKING([for $1]) -AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, -[case $MAGIC_CMD in -[[\\/*] | ?:[\\/]*]) - lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. - ;; -*) - lt_save_MAGIC_CMD="$MAGIC_CMD" - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR -dnl $ac_dummy forces splitting on constant user-supplied paths. -dnl POSIX.2 word splitting is done only on the output of word expansions, -dnl not every word. This closes a longstanding sh security hole. - ac_dummy="m4_if([$2], , $PATH, [$2])" - for ac_dir in $ac_dummy; do - IFS="$lt_save_ifs" - test -z "$ac_dir" && ac_dir=. - if test -f $ac_dir/$1; then - lt_cv_path_MAGIC_CMD="$ac_dir/$1" - if test -n "$file_magic_test_file"; then - case $deplibs_check_method in - "file_magic "*) - file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` - MAGIC_CMD="$lt_cv_path_MAGIC_CMD" - if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | - $EGREP "$file_magic_regex" > /dev/null; then - : - else - cat <<_LT_EOF 1>&2 - -*** Warning: the command libtool uses to detect shared libraries, -*** $file_magic_cmd, produces output that libtool cannot recognize. -*** The result is that libtool may fail to recognize shared libraries -*** as such. This will affect the creation of libtool libraries that -*** depend on shared libraries, but programs linked with such libtool -*** libraries will work regardless of this problem. Nevertheless, you -*** may want to report the problem to your system manager and/or to -*** bug-libtool@gnu.org - -_LT_EOF - fi ;; - esac - fi - break - fi - done - IFS="$lt_save_ifs" - MAGIC_CMD="$lt_save_MAGIC_CMD" - ;; -esac]) -MAGIC_CMD="$lt_cv_path_MAGIC_CMD" -if test -n "$MAGIC_CMD"; then - AC_MSG_RESULT($MAGIC_CMD) -else - AC_MSG_RESULT(no) -fi -_LT_DECL([], [MAGIC_CMD], [0], - [Used to examine libraries when file_magic_cmd begins with "file"])dnl -])# _LT_PATH_TOOL_PREFIX - -# Old name: -AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX]) -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], []) - - -# _LT_PATH_MAGIC -# -------------- -# find a file program which can recognize a shared library -m4_defun([_LT_PATH_MAGIC], -[_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) -if test -z "$lt_cv_path_MAGIC_CMD"; then - if test -n "$ac_tool_prefix"; then - _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH) - else - MAGIC_CMD=: - fi -fi -])# _LT_PATH_MAGIC - - -# LT_PATH_LD -# ---------- -# find the pathname to the GNU or non-GNU linker -AC_DEFUN([LT_PATH_LD], -[AC_REQUIRE([AC_PROG_CC])dnl -AC_REQUIRE([AC_CANONICAL_HOST])dnl -AC_REQUIRE([AC_CANONICAL_BUILD])dnl -m4_require([_LT_DECL_SED])dnl -m4_require([_LT_DECL_EGREP])dnl - -AC_ARG_WITH([gnu-ld], - [AS_HELP_STRING([--with-gnu-ld], - [assume the C compiler uses GNU ld @<:@default=no@:>@])], - [test "$withval" = no || with_gnu_ld=yes], - [with_gnu_ld=no])dnl - -ac_prog=ld -if test "$GCC" = yes; then - # Check if gcc -print-prog-name=ld gives a path. - AC_MSG_CHECKING([for ld used by $CC]) - case $host in - *-*-mingw*) - # gcc leaves a trailing carriage return which upsets mingw - ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; - *) - ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; - esac - case $ac_prog in - # Accept absolute paths. - [[\\/]]* | ?:[[\\/]]*) - re_direlt='/[[^/]][[^/]]*/\.\./' - # Canonicalize the pathname of ld - ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` - while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do - ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` - done - test -z "$LD" && LD="$ac_prog" - ;; - "") - # If it fails, then pretend we aren't using GCC. - ac_prog=ld - ;; - *) - # If it is relative, then search for the first ld in PATH. - with_gnu_ld=unknown - ;; - esac -elif test "$with_gnu_ld" = yes; then - AC_MSG_CHECKING([for GNU ld]) -else - AC_MSG_CHECKING([for non-GNU ld]) -fi -AC_CACHE_VAL(lt_cv_path_LD, -[if test -z "$LD"; then - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR - for ac_dir in $PATH; do - IFS="$lt_save_ifs" - test -z "$ac_dir" && ac_dir=. - if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then - lt_cv_path_LD="$ac_dir/$ac_prog" - # Check to see if the program is GNU ld. I'd rather use --version, - # but apparently some variants of GNU ld only accept -v. - # Break only if it was the GNU/non-GNU ld that we prefer. - case `"$lt_cv_path_LD" -v 2>&1 &1 /dev/null 2>&1; then - lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' - lt_cv_file_magic_cmd='func_win32_libid' - else - lt_cv_deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?' - lt_cv_file_magic_cmd='$OBJDUMP -f' - fi - ;; - -cegcc) - # use the weaker test based on 'objdump'. See mingw*. - lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' - lt_cv_file_magic_cmd='$OBJDUMP -f' - ;; - -darwin* | rhapsody*) - lt_cv_deplibs_check_method=pass_all - ;; - -freebsd* | dragonfly*) - if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then - case $host_cpu in - i*86 ) - # Not sure whether the presence of OpenBSD here was a mistake. - # Let's accept both of them until this is cleared up. - lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library' - lt_cv_file_magic_cmd=/usr/bin/file - lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` - ;; - esac - else - lt_cv_deplibs_check_method=pass_all - fi - ;; - -gnu*) - lt_cv_deplibs_check_method=pass_all - ;; - -hpux10.20* | hpux11*) - lt_cv_file_magic_cmd=/usr/bin/file - case $host_cpu in - ia64*) - lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' - lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so - ;; - hppa*64*) - [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - PA-RISC [0-9].[0-9]'] - lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl - ;; - *) - lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]].[[0-9]]) shared library' - lt_cv_file_magic_test_file=/usr/lib/libc.sl - ;; - esac - ;; - -interix[[3-9]]*) - # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here - lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$' - ;; - -irix5* | irix6* | nonstopux*) - case $LD in - *-32|*"-32 ") libmagic=32-bit;; - *-n32|*"-n32 ") libmagic=N32;; - *-64|*"-64 ") libmagic=64-bit;; - *) libmagic=never-match;; - esac - lt_cv_deplibs_check_method=pass_all - ;; - -# This must be Linux ELF. -linux* | k*bsd*-gnu | kopensolaris*-gnu) - lt_cv_deplibs_check_method=pass_all - ;; - -netbsd* | netbsdelf*-gnu) - if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then - lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' - else - lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$' - fi - ;; - -newos6*) - lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' - lt_cv_file_magic_cmd=/usr/bin/file - lt_cv_file_magic_test_file=/usr/lib/libnls.so - ;; - -*nto* | *qnx*) - lt_cv_deplibs_check_method=pass_all - ;; - -openbsd*) - if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$' - else - lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' - fi - ;; - -osf3* | osf4* | osf5*) - lt_cv_deplibs_check_method=pass_all - ;; - -rdos*) - lt_cv_deplibs_check_method=pass_all - ;; - -solaris*) - lt_cv_deplibs_check_method=pass_all - ;; - -sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) - lt_cv_deplibs_check_method=pass_all - ;; - -sysv4 | sysv4.3*) - case $host_vendor in - motorola) - lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' - lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` - ;; - ncr) - lt_cv_deplibs_check_method=pass_all - ;; - sequent) - lt_cv_file_magic_cmd='/bin/file' - lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' - ;; - sni) - lt_cv_file_magic_cmd='/bin/file' - lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib" - lt_cv_file_magic_test_file=/lib/libc.so - ;; - siemens) - lt_cv_deplibs_check_method=pass_all - ;; - pc) - lt_cv_deplibs_check_method=pass_all - ;; - esac - ;; - -tpf*) - lt_cv_deplibs_check_method=pass_all - ;; -esac -]) -file_magic_cmd=$lt_cv_file_magic_cmd -deplibs_check_method=$lt_cv_deplibs_check_method -test -z "$deplibs_check_method" && deplibs_check_method=unknown - -_LT_DECL([], [deplibs_check_method], [1], - [Method to check whether dependent libraries are shared objects]) -_LT_DECL([], [file_magic_cmd], [1], - [Command to use when deplibs_check_method == "file_magic"]) -])# _LT_CHECK_MAGIC_METHOD - - -# LT_PATH_NM -# ---------- -# find the pathname to a BSD- or MS-compatible name lister -AC_DEFUN([LT_PATH_NM], -[AC_REQUIRE([AC_PROG_CC])dnl -AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM, -[if test -n "$NM"; then - # Let the user override the test. - lt_cv_path_NM="$NM" -else - lt_nm_to_check="${ac_tool_prefix}nm" - if test -n "$ac_tool_prefix" && test "$build" = "$host"; then - lt_nm_to_check="$lt_nm_to_check nm" - fi - for lt_tmp_nm in $lt_nm_to_check; do - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR - for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do - IFS="$lt_save_ifs" - test -z "$ac_dir" && ac_dir=. - tmp_nm="$ac_dir/$lt_tmp_nm" - if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then - # Check to see if the nm accepts a BSD-compat flag. - # Adding the `sed 1q' prevents false positives on HP-UX, which says: - # nm: unknown option "B" ignored - # Tru64's nm complains that /dev/null is an invalid object file - case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in - */dev/null* | *'Invalid file or object type'*) - lt_cv_path_NM="$tmp_nm -B" - break - ;; - *) - case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in - */dev/null*) - lt_cv_path_NM="$tmp_nm -p" - break - ;; - *) - lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but - continue # so that we can try to find one that supports BSD flags - ;; - esac - ;; - esac - fi - done - IFS="$lt_save_ifs" - done - : ${lt_cv_path_NM=no} -fi]) -if test "$lt_cv_path_NM" != "no"; then - NM="$lt_cv_path_NM" -else - # Didn't find any BSD compatible name lister, look for dumpbin. - AC_CHECK_TOOLS(DUMPBIN, ["dumpbin -symbols" "link -dump -symbols"], :) - AC_SUBST([DUMPBIN]) - if test "$DUMPBIN" != ":"; then - NM="$DUMPBIN" - fi -fi -test -z "$NM" && NM=nm -AC_SUBST([NM]) -_LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl - -AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface], - [lt_cv_nm_interface="BSD nm" - echo "int some_variable = 0;" > conftest.$ac_ext - (eval echo "\"\$as_me:__oline__: $ac_compile\"" >&AS_MESSAGE_LOG_FD) - (eval "$ac_compile" 2>conftest.err) - cat conftest.err >&AS_MESSAGE_LOG_FD - (eval echo "\"\$as_me:__oline__: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD) - (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) - cat conftest.err >&AS_MESSAGE_LOG_FD - (eval echo "\"\$as_me:__oline__: output\"" >&AS_MESSAGE_LOG_FD) - cat conftest.out >&AS_MESSAGE_LOG_FD - if $GREP 'External.*some_variable' conftest.out > /dev/null; then - lt_cv_nm_interface="MS dumpbin" - fi - rm -f conftest*]) -])# LT_PATH_NM - -# Old names: -AU_ALIAS([AM_PROG_NM], [LT_PATH_NM]) -AU_ALIAS([AC_PROG_NM], [LT_PATH_NM]) -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([AM_PROG_NM], []) -dnl AC_DEFUN([AC_PROG_NM], []) - - -# LT_LIB_M -# -------- -# check for math library -AC_DEFUN([LT_LIB_M], -[AC_REQUIRE([AC_CANONICAL_HOST])dnl -LIBM= -case $host in -*-*-beos* | *-*-cygwin* | *-*-pw32* | *-*-darwin*) - # These system don't have libm, or don't need it - ;; -*-ncr-sysv4.3*) - AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw") - AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") - ;; -*) - AC_CHECK_LIB(m, cos, LIBM="-lm") - ;; -esac -AC_SUBST([LIBM]) -])# LT_LIB_M - -# Old name: -AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M]) -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([AC_CHECK_LIBM], []) - - -# _LT_COMPILER_NO_RTTI([TAGNAME]) -# ------------------------------- -m4_defun([_LT_COMPILER_NO_RTTI], -[m4_require([_LT_TAG_COMPILER])dnl - -_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= - -if test "$GCC" = yes; then - _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' - - _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], - lt_cv_prog_compiler_rtti_exceptions, - [-fno-rtti -fno-exceptions], [], - [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"]) -fi -_LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1], - [Compiler flag to turn off builtin functions]) -])# _LT_COMPILER_NO_RTTI - - -# _LT_CMD_GLOBAL_SYMBOLS -# ---------------------- -m4_defun([_LT_CMD_GLOBAL_SYMBOLS], -[AC_REQUIRE([AC_CANONICAL_HOST])dnl -AC_REQUIRE([AC_PROG_CC])dnl -AC_REQUIRE([LT_PATH_NM])dnl -AC_REQUIRE([LT_PATH_LD])dnl -m4_require([_LT_DECL_SED])dnl -m4_require([_LT_DECL_EGREP])dnl -m4_require([_LT_TAG_COMPILER])dnl - -# Check for command to grab the raw symbol name followed by C symbol from nm. -AC_MSG_CHECKING([command to parse $NM output from $compiler object]) -AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], -[ -# These are sane defaults that work on at least a few old systems. -# [They come from Ultrix. What could be older than Ultrix?!! ;)] - -# Character class describing NM global symbol codes. -symcode='[[BCDEGRST]]' - -# Regexp to match symbols that can be accessed directly from C. -sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)' - -# Define system-specific variables. -case $host_os in -aix*) - symcode='[[BCDT]]' - ;; -cygwin* | mingw* | pw32* | cegcc*) - symcode='[[ABCDGISTW]]' - ;; -hpux*) - if test "$host_cpu" = ia64; then - symcode='[[ABCDEGRST]]' - fi - ;; -irix* | nonstopux*) - symcode='[[BCDEGRST]]' - ;; -osf*) - symcode='[[BCDEGQRST]]' - ;; -solaris*) - symcode='[[BDRT]]' - ;; -sco3.2v5*) - symcode='[[DT]]' - ;; -sysv4.2uw2*) - symcode='[[DT]]' - ;; -sysv5* | sco5v6* | unixware* | OpenUNIX*) - symcode='[[ABDT]]' - ;; -sysv4) - symcode='[[DFNSTU]]' - ;; -esac - -# If we're using GNU nm, then use its standard symbol codes. -case `$NM -V 2>&1` in -*GNU* | *'with BFD'*) - symcode='[[ABCDGIRSTW]]' ;; -esac - -# Transform an extracted symbol line into a proper C declaration. -# Some systems (esp. on ia64) link data and code symbols differently, -# so use this general approach. -lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" - -# Transform an extracted symbol line into symbol name and symbol address -lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p'" -lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([[^ ]]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \(lib[[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"lib\2\", (void *) \&\2},/p'" - -# Handle CRLF in mingw tool chain -opt_cr= -case $build_os in -mingw*) - opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp - ;; -esac - -# Try without a prefix underscore, then with it. -for ac_symprfx in "" "_"; do - - # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. - symxfrm="\\1 $ac_symprfx\\2 \\2" - - # Write the raw and C identifiers. - if test "$lt_cv_nm_interface" = "MS dumpbin"; then - # Fake it for dumpbin and say T for any non-static function - # and D for any global variable. - # Also find C++ and __fastcall symbols from MSVC++, - # which start with @ or ?. - lt_cv_sys_global_symbol_pipe="$AWK ['"\ -" {last_section=section; section=\$ 3};"\ -" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ -" \$ 0!~/External *\|/{next};"\ -" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ -" {if(hide[section]) next};"\ -" {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\ -" {split(\$ 0, a, /\||\r/); split(a[2], s)};"\ -" s[1]~/^[@?]/{print s[1], s[1]; next};"\ -" s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\ -" ' prfx=^$ac_symprfx]" - else - lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" - fi - - # Check to see that the pipe works correctly. - pipe_works=no - - rm -f conftest* - cat > conftest.$ac_ext <<_LT_EOF -#ifdef __cplusplus -extern "C" { -#endif -char nm_test_var; -void nm_test_func(void); -void nm_test_func(void){} -#ifdef __cplusplus -} -#endif -int main(){nm_test_var='a';nm_test_func();return(0);} -_LT_EOF - - if AC_TRY_EVAL(ac_compile); then - # Now try to grab the symbols. - nlist=conftest.nm - if AC_TRY_EVAL(NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist) && test -s "$nlist"; then - # Try sorting and uniquifying the output. - if sort "$nlist" | uniq > "$nlist"T; then - mv -f "$nlist"T "$nlist" - else - rm -f "$nlist"T - fi - - # Make sure that we snagged all the symbols we need. - if $GREP ' nm_test_var$' "$nlist" >/dev/null; then - if $GREP ' nm_test_func$' "$nlist" >/dev/null; then - cat <<_LT_EOF > conftest.$ac_ext -#ifdef __cplusplus -extern "C" { -#endif - -_LT_EOF - # Now generate the symbol file. - eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' - - cat <<_LT_EOF >> conftest.$ac_ext - -/* The mapping between symbol names and symbols. */ -const struct { - const char *name; - void *address; -} -lt__PROGRAM__LTX_preloaded_symbols[[]] = -{ - { "@PROGRAM@", (void *) 0 }, -_LT_EOF - $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext - cat <<\_LT_EOF >> conftest.$ac_ext - {0, (void *) 0} -}; - -/* This works around a problem in FreeBSD linker */ -#ifdef FREEBSD_WORKAROUND -static const void *lt_preloaded_setup() { - return lt__PROGRAM__LTX_preloaded_symbols; -} -#endif - -#ifdef __cplusplus -} -#endif -_LT_EOF - # Now try linking the two files. - mv conftest.$ac_objext conftstm.$ac_objext - lt_save_LIBS="$LIBS" - lt_save_CFLAGS="$CFLAGS" - LIBS="conftstm.$ac_objext" - CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" - if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext}; then - pipe_works=yes - fi - LIBS="$lt_save_LIBS" - CFLAGS="$lt_save_CFLAGS" - else - echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD - fi - else - echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD - fi - else - echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD - fi - else - echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD - cat conftest.$ac_ext >&5 - fi - rm -rf conftest* conftst* - - # Do not use the global_symbol_pipe unless it works. - if test "$pipe_works" = yes; then - break - else - lt_cv_sys_global_symbol_pipe= - fi -done -]) -if test -z "$lt_cv_sys_global_symbol_pipe"; then - lt_cv_sys_global_symbol_to_cdecl= -fi -if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then - AC_MSG_RESULT(failed) -else - AC_MSG_RESULT(ok) -fi - -_LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1], - [Take the output of nm and produce a listing of raw symbols and C names]) -_LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1], - [Transform the output of nm in a proper C declaration]) -_LT_DECL([global_symbol_to_c_name_address], - [lt_cv_sys_global_symbol_to_c_name_address], [1], - [Transform the output of nm in a C name address pair]) -_LT_DECL([global_symbol_to_c_name_address_lib_prefix], - [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1], - [Transform the output of nm in a C name address pair when lib prefix is needed]) -]) # _LT_CMD_GLOBAL_SYMBOLS - - -# _LT_COMPILER_PIC([TAGNAME]) -# --------------------------- -m4_defun([_LT_COMPILER_PIC], -[m4_require([_LT_TAG_COMPILER])dnl -_LT_TAGVAR(lt_prog_compiler_wl, $1)= -_LT_TAGVAR(lt_prog_compiler_pic, $1)= -_LT_TAGVAR(lt_prog_compiler_static, $1)= - -AC_MSG_CHECKING([for $compiler option to produce PIC]) -m4_if([$1], [CXX], [ - # C++ specific cases for pic, static, wl, etc. - if test "$GXX" = yes; then - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' - - case $host_os in - aix*) - # All AIX code is PIC. - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - fi - ;; - - amigaos*) - case $host_cpu in - powerpc) - # see comment about AmigaOS4 .so support - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' - ;; - m68k) - # FIXME: we need at least 68020 code to build shared libraries, but - # adding the `-m68020' flag to GCC prevents building anything better, - # like `-m68040'. - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' - ;; - esac - ;; - - beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) - # PIC is the default for these OSes. - ;; - mingw* | cygwin* | os2* | pw32* | cegcc*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - # Although the cygwin gcc ignores -fPIC, still need this for old-style - # (--disable-auto-import) libraries - m4_if([$1], [GCJ], [], - [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) - ;; - darwin* | rhapsody*) - # PIC is the default on this platform - # Common symbols not allowed in MH_DYLIB files - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' - ;; - *djgpp*) - # DJGPP does not support shared libraries at all - _LT_TAGVAR(lt_prog_compiler_pic, $1)= - ;; - interix[[3-9]]*) - # Interix 3.x gcc -fpic/-fPIC options generate broken code. - # Instead, we relocate shared libraries at runtime. - ;; - sysv4*MP*) - if test -d /usr/nec; then - _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic - fi - ;; - hpux*) - # PIC is the default for 64-bit PA HP-UX, but not for 32-bit - # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag - # sets the default TLS model and affects inlining. - case $host_cpu in - hppa*64*) - ;; - *) - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' - ;; - esac - ;; - *qnx* | *nto*) - # QNX uses GNU C++, but need to define -shared option too, otherwise - # it will coredump. - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' - ;; - *) - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' - ;; - esac - else - case $host_os in - aix[[4-9]]*) - # All AIX code is PIC. - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - else - _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' - fi - ;; - chorus*) - case $cc_basename in - cxch68*) - # Green Hills C++ Compiler - # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" - ;; - esac - ;; - dgux*) - case $cc_basename in - ec++*) - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - ;; - ghcx*) - # Green Hills C++ Compiler - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' - ;; - *) - ;; - esac - ;; - freebsd* | dragonfly*) - # FreeBSD uses GNU C++ - ;; - hpux9* | hpux10* | hpux11*) - case $cc_basename in - CC*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' - if test "$host_cpu" != ia64; then - _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' - fi - ;; - aCC*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' - case $host_cpu in - hppa*64*|ia64*) - # +Z the default - ;; - *) - _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' - ;; - esac - ;; - *) - ;; - esac - ;; - interix*) - # This is c89, which is MS Visual C++ (no shared libs) - # Anyone wants to do a port? - ;; - irix5* | irix6* | nonstopux*) - case $cc_basename in - CC*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' - # CC pic flag -KPIC is the default. - ;; - *) - ;; - esac - ;; - linux* | k*bsd*-gnu | kopensolaris*-gnu) - case $cc_basename in - KCC*) - # KAI C++ Compiler - _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' - ;; - ecpc* ) - # old Intel C++ for x86_64 which still supported -KPIC. - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' - ;; - icpc* ) - # Intel C++, used to be incompatible with GCC. - # ICC 10 doesn't accept -KPIC any more. - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' - ;; - pgCC* | pgcpp*) - # Portland Group C++ compiler - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - cxx*) - # Compaq C++ - # Make sure the PIC flag is empty. It appears that all Alpha - # Linux and Compaq Tru64 Unix objects are PIC. - _LT_TAGVAR(lt_prog_compiler_pic, $1)= - _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' - ;; - xlc* | xlC*) - # IBM XL 8.0 on PPC - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' - ;; - *) - case `$CC -V 2>&1 | sed 5q` in - *Sun\ C*) - # Sun C++ 5.9 - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' - ;; - esac - ;; - esac - ;; - lynxos*) - ;; - m88k*) - ;; - mvs*) - case $cc_basename in - cxx*) - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall' - ;; - *) - ;; - esac - ;; - netbsd* | netbsdelf*-gnu) - ;; - *qnx* | *nto*) - # QNX uses GNU C++, but need to define -shared option too, otherwise - # it will coredump. - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' - ;; - osf3* | osf4* | osf5*) - case $cc_basename in - KCC*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' - ;; - RCC*) - # Rational C++ 2.4.1 - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' - ;; - cxx*) - # Digital/Compaq C++ - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - # Make sure the PIC flag is empty. It appears that all Alpha - # Linux and Compaq Tru64 Unix objects are PIC. - _LT_TAGVAR(lt_prog_compiler_pic, $1)= - _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' - ;; - *) - ;; - esac - ;; - psos*) - ;; - solaris*) - case $cc_basename in - CC*) - # Sun C++ 4.2, 5.x and Centerline C++ - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' - ;; - gcx*) - # Green Hills C++ Compiler - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' - ;; - *) - ;; - esac - ;; - sunos4*) - case $cc_basename in - CC*) - # Sun C++ 4.x - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - lcc*) - # Lucid - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' - ;; - *) - ;; - esac - ;; - sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) - case $cc_basename in - CC*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - esac - ;; - tandem*) - case $cc_basename in - NCC*) - # NonStop-UX NCC 3.20 - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - ;; - *) - ;; - esac - ;; - vxworks*) - ;; - *) - _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no - ;; - esac - fi -], -[ - if test "$GCC" = yes; then - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' - - case $host_os in - aix*) - # All AIX code is PIC. - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - fi - ;; - - amigaos*) - case $host_cpu in - powerpc) - # see comment about AmigaOS4 .so support - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' - ;; - m68k) - # FIXME: we need at least 68020 code to build shared libraries, but - # adding the `-m68020' flag to GCC prevents building anything better, - # like `-m68040'. - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' - ;; - esac - ;; - - beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) - # PIC is the default for these OSes. - ;; - - mingw* | cygwin* | pw32* | os2* | cegcc*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - # Although the cygwin gcc ignores -fPIC, still need this for old-style - # (--disable-auto-import) libraries - m4_if([$1], [GCJ], [], - [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) - ;; - - darwin* | rhapsody*) - # PIC is the default on this platform - # Common symbols not allowed in MH_DYLIB files - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' - ;; - - hpux*) - # PIC is the default for 64-bit PA HP-UX, but not for 32-bit - # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag - # sets the default TLS model and affects inlining. - case $host_cpu in - hppa*64*) - # +Z the default - ;; - *) - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' - ;; - esac - ;; - - interix[[3-9]]*) - # Interix 3.x gcc -fpic/-fPIC options generate broken code. - # Instead, we relocate shared libraries at runtime. - ;; - - msdosdjgpp*) - # Just because we use GCC doesn't mean we suddenly get shared libraries - # on systems that don't support them. - _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no - enable_shared=no - ;; - - *nto* | *qnx*) - # QNX uses GNU C++, but need to define -shared option too, otherwise - # it will coredump. - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' - ;; - - sysv4*MP*) - if test -d /usr/nec; then - _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic - fi - ;; - - *) - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' - ;; - esac - else - # PORTME Check for flag to pass linker flags through the system compiler. - case $host_os in - aix*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - else - _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' - fi - ;; - - mingw* | cygwin* | pw32* | os2* | cegcc*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - m4_if([$1], [GCJ], [], - [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) - ;; - - hpux9* | hpux10* | hpux11*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but - # not for PA HP-UX. - case $host_cpu in - hppa*64*|ia64*) - # +Z the default - ;; - *) - _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' - ;; - esac - # Is there a better lt_prog_compiler_static that works with the bundled CC? - _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' - ;; - - irix5* | irix6* | nonstopux*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - # PIC (with -KPIC) is the default. - _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' - ;; - - linux* | k*bsd*-gnu | kopensolaris*-gnu) - case $cc_basename in - # old Intel for x86_64 which still supported -KPIC. - ecc*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' - ;; - # icc used to be incompatible with GCC. - # ICC 10 doesn't accept -KPIC any more. - icc* | ifort*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' - ;; - # Lahey Fortran 8.1. - lf95*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared' - _LT_TAGVAR(lt_prog_compiler_static, $1)='--static' - ;; - pgcc* | pgf77* | pgf90* | pgf95*) - # Portland Group compilers (*not* the Pentium gcc compiler, - # which looks to be a dead project) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - ccc*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - # All Alpha code is PIC. - _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' - ;; - xl*) - # IBM XL C 8.0/Fortran 10.1 on PPC - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' - ;; - *) - case `$CC -V 2>&1 | sed 5q` in - *Sun\ C*) - # Sun C 5.9 - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - ;; - *Sun\ F*) - # Sun Fortran 8.3 passes all unrecognized flags to the linker - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - _LT_TAGVAR(lt_prog_compiler_wl, $1)='' - ;; - esac - ;; - esac - ;; - - newsos6) - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - - *nto* | *qnx*) - # QNX uses GNU C++, but need to define -shared option too, otherwise - # it will coredump. - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' - ;; - - osf3* | osf4* | osf5*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - # All OSF/1 code is PIC. - _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' - ;; - - rdos*) - _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' - ;; - - solaris*) - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - case $cc_basename in - f77* | f90* | f95*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';; - *) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';; - esac - ;; - - sunos4*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - - sysv4 | sysv4.2uw2* | sysv4.3*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - - sysv4*MP*) - if test -d /usr/nec ;then - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - fi - ;; - - sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - - unicos*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no - ;; - - uts4*) - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - - *) - _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no - ;; - esac - fi -]) -case $host_os in - # For platforms which do not support PIC, -DPIC is meaningless: - *djgpp*) - _LT_TAGVAR(lt_prog_compiler_pic, $1)= - ;; - *) - _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])" - ;; -esac -AC_MSG_RESULT([$_LT_TAGVAR(lt_prog_compiler_pic, $1)]) -_LT_TAGDECL([wl], [lt_prog_compiler_wl], [1], - [How to pass a linker flag through the compiler]) - -# -# Check to make sure the PIC flag actually works. -# -if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then - _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works], - [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)], - [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [], - [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in - "" | " "*) ;; - *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;; - esac], - [_LT_TAGVAR(lt_prog_compiler_pic, $1)= - _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) -fi -_LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1], - [Additional compiler flags for building library objects]) - -# -# Check to make sure the static flag actually works. -# -wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\" -_LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], - _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1), - $lt_tmp_static_flag, - [], - [_LT_TAGVAR(lt_prog_compiler_static, $1)=]) -_LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1], - [Compiler flag to prevent dynamic linking]) -])# _LT_COMPILER_PIC - - -# _LT_LINKER_SHLIBS([TAGNAME]) -# ---------------------------- -# See if the linker supports building shared libraries. -m4_defun([_LT_LINKER_SHLIBS], -[AC_REQUIRE([LT_PATH_LD])dnl -AC_REQUIRE([LT_PATH_NM])dnl -m4_require([_LT_FILEUTILS_DEFAULTS])dnl -m4_require([_LT_DECL_EGREP])dnl -m4_require([_LT_DECL_SED])dnl -m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl -m4_require([_LT_TAG_COMPILER])dnl -AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) -m4_if([$1], [CXX], [ - _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' - case $host_os in - aix[[4-9]]*) - # If we're using GNU nm, then we don't want the "-C" option. - # -C means demangle to AIX nm, but means don't demangle with GNU nm - if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then - _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' - else - _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' - fi - ;; - pw32*) - _LT_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds" - ;; - cygwin* | mingw* | cegcc*) - _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;/^.*[[ ]]__nm__/s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' - ;; - linux* | k*bsd*-gnu) - _LT_TAGVAR(link_all_deplibs, $1)=no - ;; - *) - _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' - ;; - esac - _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] -], [ - runpath_var= - _LT_TAGVAR(allow_undefined_flag, $1)= - _LT_TAGVAR(always_export_symbols, $1)=no - _LT_TAGVAR(archive_cmds, $1)= - _LT_TAGVAR(archive_expsym_cmds, $1)= - _LT_TAGVAR(compiler_needs_object, $1)=no - _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no - _LT_TAGVAR(export_dynamic_flag_spec, $1)= - _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' - _LT_TAGVAR(hardcode_automatic, $1)=no - _LT_TAGVAR(hardcode_direct, $1)=no - _LT_TAGVAR(hardcode_direct_absolute, $1)=no - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= - _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= - _LT_TAGVAR(hardcode_libdir_separator, $1)= - _LT_TAGVAR(hardcode_minus_L, $1)=no - _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported - _LT_TAGVAR(inherit_rpath, $1)=no - _LT_TAGVAR(link_all_deplibs, $1)=unknown - _LT_TAGVAR(module_cmds, $1)= - _LT_TAGVAR(module_expsym_cmds, $1)= - _LT_TAGVAR(old_archive_from_new_cmds, $1)= - _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)= - _LT_TAGVAR(thread_safe_flag_spec, $1)= - _LT_TAGVAR(whole_archive_flag_spec, $1)= - # include_expsyms should be a list of space-separated symbols to be *always* - # included in the symbol list - _LT_TAGVAR(include_expsyms, $1)= - # exclude_expsyms can be an extended regexp of symbols to exclude - # it will be wrapped by ` (' and `)$', so one must not match beginning or - # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', - # as well as any symbol that contains `d'. - _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] - # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out - # platforms (ab)use it in PIC code, but their linkers get confused if - # the symbol is explicitly referenced. Since portable code cannot - # rely on this symbol name, it's probably fine to never include it in - # preloaded symbol tables. - # Exclude shared library initialization/finalization symbols. -dnl Note also adjust exclude_expsyms for C++ above. - extract_expsyms_cmds= - - case $host_os in - cygwin* | mingw* | pw32* | cegcc*) - # FIXME: the MSVC++ port hasn't been tested in a loooong time - # When not using gcc, we currently assume that we are using - # Microsoft Visual C++. - if test "$GCC" != yes; then - with_gnu_ld=no - fi - ;; - interix*) - # we just hope/assume this is gcc and not c89 (= MSVC++) - with_gnu_ld=yes - ;; - openbsd*) - with_gnu_ld=no - ;; - linux* | k*bsd*-gnu) - _LT_TAGVAR(link_all_deplibs, $1)=no - ;; - esac - - _LT_TAGVAR(ld_shlibs, $1)=yes - if test "$with_gnu_ld" = yes; then - # If archive_cmds runs LD, not CC, wlarc should be empty - wlarc='${wl}' - - # Set some defaults for GNU ld with shared library support. These - # are reset later if shared libraries are not supported. Putting them - # here allows them to be overridden if necessary. - runpath_var=LD_RUN_PATH - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' - # ancient GNU ld didn't support --whole-archive et. al. - if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then - _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' - else - _LT_TAGVAR(whole_archive_flag_spec, $1)= - fi - supports_anon_versioning=no - case `$LD -v 2>&1` in - *GNU\ gold*) supports_anon_versioning=yes ;; - *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 - *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... - *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... - *\ 2.11.*) ;; # other 2.11 versions - *) supports_anon_versioning=yes ;; - esac - - # See if GNU ld supports shared libraries. - case $host_os in - aix[[3-9]]*) - # On AIX/PPC, the GNU linker is very broken - if test "$host_cpu" != ia64; then - _LT_TAGVAR(ld_shlibs, $1)=no - cat <<_LT_EOF 1>&2 - -*** Warning: the GNU linker, at least up to release 2.9.1, is reported -*** to be unable to reliably create shared libraries on AIX. -*** Therefore, libtool is disabling shared libraries support. If you -*** really care for shared libraries, you may want to modify your PATH -*** so that a non-GNU linker is found, and then restart. - -_LT_EOF - fi - ;; - - amigaos*) - case $host_cpu in - powerpc) - # see comment about AmigaOS4 .so support - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='' - ;; - m68k) - _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_TAGVAR(hardcode_minus_L, $1)=yes - ;; - esac - ;; - - beos*) - if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then - _LT_TAGVAR(allow_undefined_flag, $1)=unsupported - # Joseph Beckenbach says some releases of gcc - # support --undefined. This deserves some investigation. FIXME - _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - else - _LT_TAGVAR(ld_shlibs, $1)=no - fi - ;; - - cygwin* | mingw* | pw32* | cegcc*) - # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, - # as there is no search path for DLLs. - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_TAGVAR(allow_undefined_flag, $1)=unsupported - _LT_TAGVAR(always_export_symbols, $1)=no - _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes - _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols' - - if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - # If the export-symbols file already is a .def file (1st line - # is EXPORTS), use it as is; otherwise, prepend... - _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then - cp $export_symbols $output_objdir/$soname.def; - else - echo EXPORTS > $output_objdir/$soname.def; - cat $export_symbols >> $output_objdir/$soname.def; - fi~ - $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - else - _LT_TAGVAR(ld_shlibs, $1)=no - fi - ;; - - interix[[3-9]]*) - _LT_TAGVAR(hardcode_direct, $1)=no - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. - # Instead, shared libraries are loaded at an image base (0x10000000 by - # default) and relocated if they conflict, which is a slow very memory - # consuming and fragmenting process. To avoid this, we pick a random, - # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link - # time. Moving up from 0x10000000 also allows more sbrk(2) space. - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - ;; - - gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) - tmp_diet=no - if test "$host_os" = linux-dietlibc; then - case $cc_basename in - diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) - esac - fi - if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ - && test "$tmp_diet" = no - then - tmp_addflag= - tmp_sharedflag='-shared' - case $cc_basename,$host_cpu in - pgcc*) # Portland Group C compiler - _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' - tmp_addflag=' $pic_flag' - ;; - pgf77* | pgf90* | pgf95*) # Portland Group f77 and f90 compilers - _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' - tmp_addflag=' $pic_flag -Mnomain' ;; - ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 - tmp_addflag=' -i_dynamic' ;; - efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 - tmp_addflag=' -i_dynamic -nofor_main' ;; - ifc* | ifort*) # Intel Fortran compiler - tmp_addflag=' -nofor_main' ;; - lf95*) # Lahey Fortran 8.1 - _LT_TAGVAR(whole_archive_flag_spec, $1)= - tmp_sharedflag='--shared' ;; - xl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below) - tmp_sharedflag='-qmkshrobj' - tmp_addflag= ;; - esac - case `$CC -V 2>&1 | sed 5q` in - *Sun\ C*) # Sun C 5.9 - _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' - _LT_TAGVAR(compiler_needs_object, $1)=yes - tmp_sharedflag='-G' ;; - *Sun\ F*) # Sun Fortran 8.3 - tmp_sharedflag='-G' ;; - esac - _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - - if test "x$supports_anon_versioning" = xyes; then - _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ - cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ - echo "local: *; };" >> $output_objdir/$libname.ver~ - $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' - fi - - case $cc_basename in - xlf*) - # IBM XL Fortran 10.1 on PPC cannot create shared libs itself - _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= - _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='-rpath $libdir' - _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $compiler_flags -soname $soname -o $lib' - if test "x$supports_anon_versioning" = xyes; then - _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ - cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ - echo "local: *; };" >> $output_objdir/$libname.ver~ - $LD -shared $libobjs $deplibs $compiler_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' - fi - ;; - esac - else - _LT_TAGVAR(ld_shlibs, $1)=no - fi - ;; - - netbsd* | netbsdelf*-gnu) - if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then - _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' - wlarc= - else - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - fi - ;; - - solaris*) - if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then - _LT_TAGVAR(ld_shlibs, $1)=no - cat <<_LT_EOF 1>&2 - -*** Warning: The releases 2.8.* of the GNU linker cannot reliably -*** create shared libraries on Solaris systems. Therefore, libtool -*** is disabling shared libraries support. We urge you to upgrade GNU -*** binutils to release 2.9.1 or newer. Another option is to modify -*** your PATH or compiler configuration so that the native linker is -*** used, and then restart. - -_LT_EOF - elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - else - _LT_TAGVAR(ld_shlibs, $1)=no - fi - ;; - - sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) - case `$LD -v 2>&1` in - *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*) - _LT_TAGVAR(ld_shlibs, $1)=no - cat <<_LT_EOF 1>&2 - -*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not -*** reliably create shared libraries on SCO systems. Therefore, libtool -*** is disabling shared libraries support. We urge you to upgrade GNU -*** binutils to release 2.16.91.0.3 or newer. Another option is to modify -*** your PATH or compiler configuration so that the native linker is -*** used, and then restart. - -_LT_EOF - ;; - *) - # For security reasons, it is highly recommended that you always - # use absolute paths for naming shared libraries, and exclude the - # DT_RUNPATH tag from executables and libraries. But doing so - # requires that you compile everything twice, which is a pain. - if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - else - _LT_TAGVAR(ld_shlibs, $1)=no - fi - ;; - esac - ;; - - sunos4*) - _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' - wlarc= - _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - - *) - if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - else - _LT_TAGVAR(ld_shlibs, $1)=no - fi - ;; - esac - - if test "$_LT_TAGVAR(ld_shlibs, $1)" = no; then - runpath_var= - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= - _LT_TAGVAR(export_dynamic_flag_spec, $1)= - _LT_TAGVAR(whole_archive_flag_spec, $1)= - fi - else - # PORTME fill in a description of your system's linker (not GNU ld) - case $host_os in - aix3*) - _LT_TAGVAR(allow_undefined_flag, $1)=unsupported - _LT_TAGVAR(always_export_symbols, $1)=yes - _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' - # Note: this linker hardcodes the directories in LIBPATH if there - # are no directories specified by -L. - _LT_TAGVAR(hardcode_minus_L, $1)=yes - if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then - # Neither direct hardcoding nor static linking is supported with a - # broken collect2. - _LT_TAGVAR(hardcode_direct, $1)=unsupported - fi - ;; - - aix[[4-9]]*) - if test "$host_cpu" = ia64; then - # On IA64, the linker does run time linking by default, so we don't - # have to do anything special. - aix_use_runtimelinking=no - exp_sym_flag='-Bexport' - no_entry_flag="" - else - # If we're using GNU nm, then we don't want the "-C" option. - # -C means demangle to AIX nm, but means don't demangle with GNU nm - if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then - _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' - else - _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' - fi - aix_use_runtimelinking=no - - # Test if we are trying to use run time linking or normal - # AIX style linking. If -brtl is somewhere in LDFLAGS, we - # need to do runtime linking. - case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) - for ld_flag in $LDFLAGS; do - if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then - aix_use_runtimelinking=yes - break - fi - done - ;; - esac - - exp_sym_flag='-bexport' - no_entry_flag='-bnoentry' - fi - - # When large executables or shared objects are built, AIX ld can - # have problems creating the table of contents. If linking a library - # or program results in "error TOC overflow" add -mminimal-toc to - # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not - # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. - - _LT_TAGVAR(archive_cmds, $1)='' - _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_direct_absolute, $1)=yes - _LT_TAGVAR(hardcode_libdir_separator, $1)=':' - _LT_TAGVAR(link_all_deplibs, $1)=yes - _LT_TAGVAR(file_list_spec, $1)='${wl}-f,' - - if test "$GCC" = yes; then - case $host_os in aix4.[[012]]|aix4.[[012]].*) - # We only want to do this on AIX 4.2 and lower, the check - # below for broken collect2 doesn't work under 4.3+ - collect2name=`${CC} -print-prog-name=collect2` - if test -f "$collect2name" && - strings "$collect2name" | $GREP resolve_lib_name >/dev/null - then - # We have reworked collect2 - : - else - # We have old collect2 - _LT_TAGVAR(hardcode_direct, $1)=unsupported - # It fails to find uninstalled libraries when the uninstalled - # path is not listed in the libpath. Setting hardcode_minus_L - # to unsupported forces relinking - _LT_TAGVAR(hardcode_minus_L, $1)=yes - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_TAGVAR(hardcode_libdir_separator, $1)= - fi - ;; - esac - shared_flag='-shared' - if test "$aix_use_runtimelinking" = yes; then - shared_flag="$shared_flag "'${wl}-G' - fi - _LT_TAGVAR(link_all_deplibs, $1)=no - else - # not using gcc - if test "$host_cpu" = ia64; then - # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release - # chokes on -Wl,-G. The following line is correct: - shared_flag='-G' - else - if test "$aix_use_runtimelinking" = yes; then - shared_flag='${wl}-G' - else - shared_flag='${wl}-bM:SRE' - fi - fi - fi - - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall' - # It seems that -bexpall does not export symbols beginning with - # underscore (_), so it is better to generate a list of symbols to export. - _LT_TAGVAR(always_export_symbols, $1)=yes - if test "$aix_use_runtimelinking" = yes; then - # Warning - without using the other runtime loading flags (-brtl), - # -berok will link without error, but may produce a broken library. - _LT_TAGVAR(allow_undefined_flag, $1)='-berok' - # Determine the default libpath from the value encoded in an - # empty executable. - _LT_SYS_MODULE_PATH_AIX - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" - else - if test "$host_cpu" = ia64; then - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' - _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" - _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" - else - # Determine the default libpath from the value encoded in an - # empty executable. - _LT_SYS_MODULE_PATH_AIX - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" - # Warning - without using the other run time loading flags, - # -berok will link without error, but may produce a broken library. - _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' - _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' - # Exported symbols can be pulled into shared objects from archives - _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' - _LT_TAGVAR(archive_cmds_need_lc, $1)=yes - # This is similar to how AIX traditionally builds its shared libraries. - _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' - fi - fi - ;; - - amigaos*) - case $host_cpu in - powerpc) - # see comment about AmigaOS4 .so support - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='' - ;; - m68k) - _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_TAGVAR(hardcode_minus_L, $1)=yes - ;; - esac - ;; - - bsdi[[45]]*) - _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic - ;; - - cygwin* | mingw* | pw32* | cegcc*) - # When not using gcc, we currently assume that we are using - # Microsoft Visual C++. - # hardcode_libdir_flag_spec is actually meaningless, as there is - # no search path for DLLs. - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' - _LT_TAGVAR(allow_undefined_flag, $1)=unsupported - # Tell ltmain to make .lib files, not .a files. - libext=lib - # Tell ltmain to make .dll files, not .so files. - shrext_cmds=".dll" - # FIXME: Setting linknames here is a bad hack. - _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `$ECHO "X$deplibs" | $Xsed -e '\''s/ -lc$//'\''` -link -dll~linknames=' - # The linker will automatically build a .lib file if we build a DLL. - _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' - # FIXME: Should let the user specify the lib program. - _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs' - _LT_TAGVAR(fix_srcfile_path, $1)='`cygpath -w "$srcfile"`' - _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes - ;; - - darwin* | rhapsody*) - _LT_DARWIN_LINKER_FEATURES($1) - ;; - - dgux*) - _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - - freebsd1*) - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - - # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor - # support. Future versions do this automatically, but an explicit c++rt0.o - # does not break anything, and helps significantly (at the cost of a little - # extra space). - freebsd2.2*) - _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' - _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - - # Unfortunately, older versions of FreeBSD 2 do not have this feature. - freebsd2*) - _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' - _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_minus_L, $1)=yes - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - - # FreeBSD 3 and greater uses gcc -shared to do shared libraries. - freebsd* | dragonfly*) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' - _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - - hpux9*) - if test "$GCC" = yes; then - _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - else - _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - fi - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' - _LT_TAGVAR(hardcode_libdir_separator, $1)=: - _LT_TAGVAR(hardcode_direct, $1)=yes - - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - _LT_TAGVAR(hardcode_minus_L, $1)=yes - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - ;; - - hpux10*) - if test "$GCC" = yes -a "$with_gnu_ld" = no; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - else - _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' - fi - if test "$with_gnu_ld" = no; then - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' - _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='+b $libdir' - _LT_TAGVAR(hardcode_libdir_separator, $1)=: - _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_direct_absolute, $1)=yes - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - _LT_TAGVAR(hardcode_minus_L, $1)=yes - fi - ;; - - hpux11*) - if test "$GCC" = yes -a "$with_gnu_ld" = no; then - case $host_cpu in - hppa*64*) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - ia64*) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - else - case $host_cpu in - hppa*64*) - _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - ia64*) - _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - fi - if test "$with_gnu_ld" = no; then - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' - _LT_TAGVAR(hardcode_libdir_separator, $1)=: - - case $host_cpu in - hppa*64*|ia64*) - _LT_TAGVAR(hardcode_direct, $1)=no - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - *) - _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_direct_absolute, $1)=yes - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - _LT_TAGVAR(hardcode_minus_L, $1)=yes - ;; - esac - fi - ;; - - irix5* | irix6* | nonstopux*) - if test "$GCC" = yes; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - # Try to use the -exported_symbol ld option, if it does not - # work, assume that -exports_file does not work either and - # implicitly export all symbols. - save_LDFLAGS="$LDFLAGS" - LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null" - AC_LINK_IFELSE(int foo(void) {}, - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib' - ) - LDFLAGS="$save_LDFLAGS" - else - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib' - fi - _LT_TAGVAR(archive_cmds_need_lc, $1)='no' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_TAGVAR(hardcode_libdir_separator, $1)=: - _LT_TAGVAR(inherit_rpath, $1)=yes - _LT_TAGVAR(link_all_deplibs, $1)=yes - ;; - - netbsd* | netbsdelf*-gnu) - if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then - _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out - else - _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF - fi - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' - _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - - newsos6) - _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_TAGVAR(hardcode_libdir_separator, $1)=: - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - - *nto* | *qnx*) - ;; - - openbsd*) - if test -f /usr/libexec/ld.so; then - _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - _LT_TAGVAR(hardcode_direct_absolute, $1)=yes - if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - else - case $host_os in - openbsd[[01]].* | openbsd2.[[0-7]] | openbsd2.[[0-7]].*) - _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' - ;; - *) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - ;; - esac - fi - else - _LT_TAGVAR(ld_shlibs, $1)=no - fi - ;; - - os2*) - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_TAGVAR(hardcode_minus_L, $1)=yes - _LT_TAGVAR(allow_undefined_flag, $1)=unsupported - _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$ECHO DATA >> $output_objdir/$libname.def~$ECHO " SINGLE NONSHARED" >> $output_objdir/$libname.def~$ECHO EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' - _LT_TAGVAR(old_archive_from_new_cmds, $1)='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' - ;; - - osf3*) - if test "$GCC" = yes; then - _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' - _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - else - _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' - _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' - fi - _LT_TAGVAR(archive_cmds_need_lc, $1)='no' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_TAGVAR(hardcode_libdir_separator, $1)=: - ;; - - osf4* | osf5*) # as osf3* with the addition of -msym flag - if test "$GCC" = yes; then - _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' - _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - else - _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' - _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ - $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp' - - # Both c and cxx compiler support -rpath directly - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' - fi - _LT_TAGVAR(archive_cmds_need_lc, $1)='no' - _LT_TAGVAR(hardcode_libdir_separator, $1)=: - ;; - - solaris*) - _LT_TAGVAR(no_undefined_flag, $1)=' -z defs' - if test "$GCC" = yes; then - wlarc='${wl}' - _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ - $CC -shared ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' - else - case `$CC -V 2>&1` in - *"Compilers 5.0"*) - wlarc='' - _LT_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ - $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' - ;; - *) - wlarc='${wl}' - _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ - $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' - ;; - esac - fi - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - case $host_os in - solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; - *) - # The compiler driver will combine and reorder linker options, - # but understands `-z linker_flag'. GCC discards it without `$wl', - # but is careful enough not to reorder. - # Supported since Solaris 2.6 (maybe 2.5.1?) - if test "$GCC" = yes; then - _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' - else - _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' - fi - ;; - esac - _LT_TAGVAR(link_all_deplibs, $1)=yes - ;; - - sunos4*) - if test "x$host_vendor" = xsequent; then - # Use $CC to link under sequent, because it throws in some extra .o - # files that make .init and .fini sections work. - _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' - else - _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' - fi - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_minus_L, $1)=yes - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - - sysv4) - case $host_vendor in - sni) - _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true??? - ;; - siemens) - ## LD is ld it makes a PLAMLIB - ## CC just makes a GrossModule. - _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' - _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs' - _LT_TAGVAR(hardcode_direct, $1)=no - ;; - motorola) - _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie - ;; - esac - runpath_var='LD_RUN_PATH' - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - - sysv4.3*) - _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport' - ;; - - sysv4*MP*) - if test -d /usr/nec; then - _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - runpath_var=LD_RUN_PATH - hardcode_runpath_var=yes - _LT_TAGVAR(ld_shlibs, $1)=yes - fi - ;; - - sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) - _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' - _LT_TAGVAR(archive_cmds_need_lc, $1)=no - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - runpath_var='LD_RUN_PATH' - - if test "$GCC" = yes; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - else - _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - fi - ;; - - sysv5* | sco3.2v5* | sco5v6*) - # Note: We can NOT use -z defs as we might desire, because we do not - # link with -lc, and that would cause any symbols used from libc to - # always be unresolved, which means just about no library would - # ever link correctly. If we're not using GNU ld we use -z text - # though, which does catch some bad symbols but isn't as heavy-handed - # as -z defs. - _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' - _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' - _LT_TAGVAR(archive_cmds_need_lc, $1)=no - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir' - _LT_TAGVAR(hardcode_libdir_separator, $1)=':' - _LT_TAGVAR(link_all_deplibs, $1)=yes - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' - runpath_var='LD_RUN_PATH' - - if test "$GCC" = yes; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - else - _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - fi - ;; - - uts4*) - _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - - *) - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - esac - - if test x$host_vendor = xsni; then - case $host in - sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Blargedynsym' - ;; - esac - fi - fi -]) -AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) -test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no - -_LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld - -_LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl -_LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl -_LT_DECL([], [extract_expsyms_cmds], [2], - [The commands to extract the exported symbol list from a shared archive]) - -# -# Do we need to explicitly link libc? -# -case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in -x|xyes) - # Assume -lc should be added - _LT_TAGVAR(archive_cmds_need_lc, $1)=yes - - if test "$enable_shared" = yes && test "$GCC" = yes; then - case $_LT_TAGVAR(archive_cmds, $1) in - *'~'*) - # FIXME: we may have to deal with multi-command sequences. - ;; - '$CC '*) - # Test whether the compiler implicitly links with -lc since on some - # systems, -lgcc has to come before -lc. If gcc already passes -lc - # to ld, don't add -lc before -lgcc. - AC_MSG_CHECKING([whether -lc should be explicitly linked in]) - $RM conftest* - echo "$lt_simple_compile_test_code" > conftest.$ac_ext - - if AC_TRY_EVAL(ac_compile) 2>conftest.err; then - soname=conftest - lib=conftest - libobjs=conftest.$ac_objext - deplibs= - wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) - pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1) - compiler_flags=-v - linker_flags=-v - verstring= - output_objdir=. - libname=conftest - lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1) - _LT_TAGVAR(allow_undefined_flag, $1)= - if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) - then - _LT_TAGVAR(archive_cmds_need_lc, $1)=no - else - _LT_TAGVAR(archive_cmds_need_lc, $1)=yes - fi - _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag - else - cat conftest.err 1>&5 - fi - $RM conftest* - AC_MSG_RESULT([$_LT_TAGVAR(archive_cmds_need_lc, $1)]) - ;; - esac - fi - ;; -esac - -_LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0], - [Whether or not to add -lc for building shared libraries]) -_LT_TAGDECL([allow_libtool_libs_with_static_runtimes], - [enable_shared_with_static_runtimes], [0], - [Whether or not to disallow shared libs when runtime libs are static]) -_LT_TAGDECL([], [export_dynamic_flag_spec], [1], - [Compiler flag to allow reflexive dlopens]) -_LT_TAGDECL([], [whole_archive_flag_spec], [1], - [Compiler flag to generate shared objects directly from archives]) -_LT_TAGDECL([], [compiler_needs_object], [1], - [Whether the compiler copes with passing no objects directly]) -_LT_TAGDECL([], [old_archive_from_new_cmds], [2], - [Create an old-style archive from a shared archive]) -_LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2], - [Create a temporary old-style archive to link instead of a shared archive]) -_LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive]) -_LT_TAGDECL([], [archive_expsym_cmds], [2]) -_LT_TAGDECL([], [module_cmds], [2], - [Commands used to build a loadable module if different from building - a shared archive.]) -_LT_TAGDECL([], [module_expsym_cmds], [2]) -_LT_TAGDECL([], [with_gnu_ld], [1], - [Whether we are building with GNU ld or not]) -_LT_TAGDECL([], [allow_undefined_flag], [1], - [Flag that allows shared libraries with undefined symbols to be built]) -_LT_TAGDECL([], [no_undefined_flag], [1], - [Flag that enforces no undefined symbols]) -_LT_TAGDECL([], [hardcode_libdir_flag_spec], [1], - [Flag to hardcode $libdir into a binary during linking. - This must work even if $libdir does not exist]) -_LT_TAGDECL([], [hardcode_libdir_flag_spec_ld], [1], - [[If ld is used when linking, flag to hardcode $libdir into a binary - during linking. This must work even if $libdir does not exist]]) -_LT_TAGDECL([], [hardcode_libdir_separator], [1], - [Whether we need a single "-rpath" flag with a separated argument]) -_LT_TAGDECL([], [hardcode_direct], [0], - [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes - DIR into the resulting binary]) -_LT_TAGDECL([], [hardcode_direct_absolute], [0], - [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes - DIR into the resulting binary and the resulting library dependency is - "absolute", i.e impossible to change by setting ${shlibpath_var} if the - library is relocated]) -_LT_TAGDECL([], [hardcode_minus_L], [0], - [Set to "yes" if using the -LDIR flag during linking hardcodes DIR - into the resulting binary]) -_LT_TAGDECL([], [hardcode_shlibpath_var], [0], - [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR - into the resulting binary]) -_LT_TAGDECL([], [hardcode_automatic], [0], - [Set to "yes" if building a shared library automatically hardcodes DIR - into the library and all subsequent libraries and executables linked - against it]) -_LT_TAGDECL([], [inherit_rpath], [0], - [Set to yes if linker adds runtime paths of dependent libraries - to runtime path list]) -_LT_TAGDECL([], [link_all_deplibs], [0], - [Whether libtool must link a program against all its dependency libraries]) -_LT_TAGDECL([], [fix_srcfile_path], [1], - [Fix the shell variable $srcfile for the compiler]) -_LT_TAGDECL([], [always_export_symbols], [0], - [Set to "yes" if exported symbols are required]) -_LT_TAGDECL([], [export_symbols_cmds], [2], - [The commands to list exported symbols]) -_LT_TAGDECL([], [exclude_expsyms], [1], - [Symbols that should not be listed in the preloaded symbols]) -_LT_TAGDECL([], [include_expsyms], [1], - [Symbols that must always be exported]) -_LT_TAGDECL([], [prelink_cmds], [2], - [Commands necessary for linking programs (against libraries) with templates]) -_LT_TAGDECL([], [file_list_spec], [1], - [Specify filename containing input files]) -dnl FIXME: Not yet implemented -dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1], -dnl [Compiler flag to generate thread safe objects]) -])# _LT_LINKER_SHLIBS - - -# _LT_LANG_C_CONFIG([TAG]) -# ------------------------ -# Ensure that the configuration variables for a C compiler are suitably -# defined. These variables are subsequently used by _LT_CONFIG to write -# the compiler configuration to `libtool'. -m4_defun([_LT_LANG_C_CONFIG], -[m4_require([_LT_DECL_EGREP])dnl -lt_save_CC="$CC" -AC_LANG_PUSH(C) - -# Source file extension for C test sources. -ac_ext=c - -# Object file extension for compiled C test sources. -objext=o -_LT_TAGVAR(objext, $1)=$objext - -# Code to be used in simple compile tests -lt_simple_compile_test_code="int some_variable = 0;" - -# Code to be used in simple link tests -lt_simple_link_test_code='int main(){return(0);}' - -_LT_TAG_COMPILER -# Save the default compiler, since it gets overwritten when the other -# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. -compiler_DEFAULT=$CC - -# save warnings/boilerplate of simple test code -_LT_COMPILER_BOILERPLATE -_LT_LINKER_BOILERPLATE - -## CAVEAT EMPTOR: -## There is no encapsulation within the following macros, do not change -## the running order or otherwise move them around unless you know exactly -## what you are doing... -if test -n "$compiler"; then - _LT_COMPILER_NO_RTTI($1) - _LT_COMPILER_PIC($1) - _LT_COMPILER_C_O($1) - _LT_COMPILER_FILE_LOCKS($1) - _LT_LINKER_SHLIBS($1) - _LT_SYS_DYNAMIC_LINKER($1) - _LT_LINKER_HARDCODE_LIBPATH($1) - LT_SYS_DLOPEN_SELF - _LT_CMD_STRIPLIB - - # Report which library types will actually be built - AC_MSG_CHECKING([if libtool supports shared libraries]) - AC_MSG_RESULT([$can_build_shared]) - - AC_MSG_CHECKING([whether to build shared libraries]) - test "$can_build_shared" = "no" && enable_shared=no - - # On AIX, shared libraries and static libraries use the same namespace, and - # are all built from PIC. - case $host_os in - aix3*) - test "$enable_shared" = yes && enable_static=no - if test -n "$RANLIB"; then - archive_cmds="$archive_cmds~\$RANLIB \$lib" - postinstall_cmds='$RANLIB $lib' - fi - ;; - - aix[[4-9]]*) - if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then - test "$enable_shared" = yes && enable_static=no - fi - ;; - esac - AC_MSG_RESULT([$enable_shared]) - - AC_MSG_CHECKING([whether to build static libraries]) - # Make sure either enable_shared or enable_static is yes. - test "$enable_shared" = yes || enable_static=yes - AC_MSG_RESULT([$enable_static]) - - _LT_CONFIG($1) -fi -AC_LANG_POP -CC="$lt_save_CC" -])# _LT_LANG_C_CONFIG - - -# _LT_PROG_CXX -# ------------ -# Since AC_PROG_CXX is broken, in that it returns g++ if there is no c++ -# compiler, we have our own version here. -m4_defun([_LT_PROG_CXX], -[ -pushdef([AC_MSG_ERROR], [_lt_caught_CXX_error=yes]) -AC_PROG_CXX -if test -n "$CXX" && ( test "X$CXX" != "Xno" && - ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || - (test "X$CXX" != "Xg++"))) ; then - AC_PROG_CXXCPP -else - _lt_caught_CXX_error=yes -fi -popdef([AC_MSG_ERROR]) -])# _LT_PROG_CXX - -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([_LT_PROG_CXX], []) - - -# _LT_LANG_CXX_CONFIG([TAG]) -# -------------------------- -# Ensure that the configuration variables for a C++ compiler are suitably -# defined. These variables are subsequently used by _LT_CONFIG to write -# the compiler configuration to `libtool'. -m4_defun([_LT_LANG_CXX_CONFIG], -[AC_REQUIRE([_LT_PROG_CXX])dnl -m4_require([_LT_FILEUTILS_DEFAULTS])dnl -m4_require([_LT_DECL_EGREP])dnl - -AC_LANG_PUSH(C++) -_LT_TAGVAR(archive_cmds_need_lc, $1)=no -_LT_TAGVAR(allow_undefined_flag, $1)= -_LT_TAGVAR(always_export_symbols, $1)=no -_LT_TAGVAR(archive_expsym_cmds, $1)= -_LT_TAGVAR(compiler_needs_object, $1)=no -_LT_TAGVAR(export_dynamic_flag_spec, $1)= -_LT_TAGVAR(hardcode_direct, $1)=no -_LT_TAGVAR(hardcode_direct_absolute, $1)=no -_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= -_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= -_LT_TAGVAR(hardcode_libdir_separator, $1)= -_LT_TAGVAR(hardcode_minus_L, $1)=no -_LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported -_LT_TAGVAR(hardcode_automatic, $1)=no -_LT_TAGVAR(inherit_rpath, $1)=no -_LT_TAGVAR(module_cmds, $1)= -_LT_TAGVAR(module_expsym_cmds, $1)= -_LT_TAGVAR(link_all_deplibs, $1)=unknown -_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds -_LT_TAGVAR(no_undefined_flag, $1)= -_LT_TAGVAR(whole_archive_flag_spec, $1)= -_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no - -# Source file extension for C++ test sources. -ac_ext=cpp - -# Object file extension for compiled C++ test sources. -objext=o -_LT_TAGVAR(objext, $1)=$objext - -# No sense in running all these tests if we already determined that -# the CXX compiler isn't working. Some variables (like enable_shared) -# are currently assumed to apply to all compilers on this platform, -# and will be corrupted by setting them based on a non-working compiler. -if test "$_lt_caught_CXX_error" != yes; then - # Code to be used in simple compile tests - lt_simple_compile_test_code="int some_variable = 0;" - - # Code to be used in simple link tests - lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }' - - # ltmain only uses $CC for tagged configurations so make sure $CC is set. - _LT_TAG_COMPILER - - # save warnings/boilerplate of simple test code - _LT_COMPILER_BOILERPLATE - _LT_LINKER_BOILERPLATE - - # Allow CC to be a program name with arguments. - lt_save_CC=$CC - lt_save_LD=$LD - lt_save_GCC=$GCC - GCC=$GXX - lt_save_with_gnu_ld=$with_gnu_ld - lt_save_path_LD=$lt_cv_path_LD - if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then - lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx - else - $as_unset lt_cv_prog_gnu_ld - fi - if test -n "${lt_cv_path_LDCXX+set}"; then - lt_cv_path_LD=$lt_cv_path_LDCXX - else - $as_unset lt_cv_path_LD - fi - test -z "${LDCXX+set}" || LD=$LDCXX - CC=${CXX-"c++"} - compiler=$CC - _LT_TAGVAR(compiler, $1)=$CC - _LT_CC_BASENAME([$compiler]) - - if test -n "$compiler"; then - # We don't want -fno-exception when compiling C++ code, so set the - # no_builtin_flag separately - if test "$GXX" = yes; then - _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' - else - _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= - fi - - if test "$GXX" = yes; then - # Set up default GNU C++ configuration - - LT_PATH_LD - - # Check if GNU C++ uses GNU ld as the underlying linker, since the - # archiving commands below assume that GNU ld is being used. - if test "$with_gnu_ld" = yes; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' - - # If archive_cmds runs LD, not CC, wlarc should be empty - # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to - # investigate it a little bit more. (MM) - wlarc='${wl}' - - # ancient GNU ld didn't support --whole-archive et. al. - if eval "`$CC -print-prog-name=ld` --help 2>&1" | - $GREP 'no-whole-archive' > /dev/null; then - _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' - else - _LT_TAGVAR(whole_archive_flag_spec, $1)= - fi - else - with_gnu_ld=no - wlarc= - - # A generic and very simple default shared library creation - # command for GNU C++ for the case where it uses the native - # linker, instead of GNU ld. If possible, this setting should - # overridden to take advantage of the native linker features on - # the platform it is being used on. - _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' - fi - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"' - - else - GXX=no - with_gnu_ld=no - wlarc= - fi - - # PORTME: fill in a description of your system's C++ link characteristics - AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) - _LT_TAGVAR(ld_shlibs, $1)=yes - case $host_os in - aix3*) - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - aix[[4-9]]*) - if test "$host_cpu" = ia64; then - # On IA64, the linker does run time linking by default, so we don't - # have to do anything special. - aix_use_runtimelinking=no - exp_sym_flag='-Bexport' - no_entry_flag="" - else - aix_use_runtimelinking=no - - # Test if we are trying to use run time linking or normal - # AIX style linking. If -brtl is somewhere in LDFLAGS, we - # need to do runtime linking. - case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) - for ld_flag in $LDFLAGS; do - case $ld_flag in - *-brtl*) - aix_use_runtimelinking=yes - break - ;; - esac - done - ;; - esac - - exp_sym_flag='-bexport' - no_entry_flag='-bnoentry' - fi - - # When large executables or shared objects are built, AIX ld can - # have problems creating the table of contents. If linking a library - # or program results in "error TOC overflow" add -mminimal-toc to - # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not - # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. - - _LT_TAGVAR(archive_cmds, $1)='' - _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_direct_absolute, $1)=yes - _LT_TAGVAR(hardcode_libdir_separator, $1)=':' - _LT_TAGVAR(link_all_deplibs, $1)=yes - _LT_TAGVAR(file_list_spec, $1)='${wl}-f,' - - if test "$GXX" = yes; then - case $host_os in aix4.[[012]]|aix4.[[012]].*) - # We only want to do this on AIX 4.2 and lower, the check - # below for broken collect2 doesn't work under 4.3+ - collect2name=`${CC} -print-prog-name=collect2` - if test -f "$collect2name" && - strings "$collect2name" | $GREP resolve_lib_name >/dev/null - then - # We have reworked collect2 - : - else - # We have old collect2 - _LT_TAGVAR(hardcode_direct, $1)=unsupported - # It fails to find uninstalled libraries when the uninstalled - # path is not listed in the libpath. Setting hardcode_minus_L - # to unsupported forces relinking - _LT_TAGVAR(hardcode_minus_L, $1)=yes - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_TAGVAR(hardcode_libdir_separator, $1)= - fi - esac - shared_flag='-shared' - if test "$aix_use_runtimelinking" = yes; then - shared_flag="$shared_flag "'${wl}-G' - fi - else - # not using gcc - if test "$host_cpu" = ia64; then - # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release - # chokes on -Wl,-G. The following line is correct: - shared_flag='-G' - else - if test "$aix_use_runtimelinking" = yes; then - shared_flag='${wl}-G' - else - shared_flag='${wl}-bM:SRE' - fi - fi - fi - - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall' - # It seems that -bexpall does not export symbols beginning with - # underscore (_), so it is better to generate a list of symbols to - # export. - _LT_TAGVAR(always_export_symbols, $1)=yes - if test "$aix_use_runtimelinking" = yes; then - # Warning - without using the other runtime loading flags (-brtl), - # -berok will link without error, but may produce a broken library. - _LT_TAGVAR(allow_undefined_flag, $1)='-berok' - # Determine the default libpath from the value encoded in an empty - # executable. - _LT_SYS_MODULE_PATH_AIX - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" - - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" - else - if test "$host_cpu" = ia64; then - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' - _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" - _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" - else - # Determine the default libpath from the value encoded in an - # empty executable. - _LT_SYS_MODULE_PATH_AIX - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" - # Warning - without using the other run time loading flags, - # -berok will link without error, but may produce a broken library. - _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' - _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' - # Exported symbols can be pulled into shared objects from archives - _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' - _LT_TAGVAR(archive_cmds_need_lc, $1)=yes - # This is similar to how AIX traditionally builds its shared - # libraries. - _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' - fi - fi - ;; - - beos*) - if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then - _LT_TAGVAR(allow_undefined_flag, $1)=unsupported - # Joseph Beckenbach says some releases of gcc - # support --undefined. This deserves some investigation. FIXME - _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - else - _LT_TAGVAR(ld_shlibs, $1)=no - fi - ;; - - chorus*) - case $cc_basename in - *) - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - esac - ;; - - cygwin* | mingw* | pw32* | cegcc*) - # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, - # as there is no search path for DLLs. - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_TAGVAR(allow_undefined_flag, $1)=unsupported - _LT_TAGVAR(always_export_symbols, $1)=no - _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes - - if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - # If the export-symbols file already is a .def file (1st line - # is EXPORTS), use it as is; otherwise, prepend... - _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then - cp $export_symbols $output_objdir/$soname.def; - else - echo EXPORTS > $output_objdir/$soname.def; - cat $export_symbols >> $output_objdir/$soname.def; - fi~ - $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - else - _LT_TAGVAR(ld_shlibs, $1)=no - fi - ;; - darwin* | rhapsody*) - _LT_DARWIN_LINKER_FEATURES($1) - ;; - - dgux*) - case $cc_basename in - ec++*) - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - ghcx*) - # Green Hills C++ Compiler - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - *) - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - esac - ;; - - freebsd[[12]]*) - # C++ shared libraries reported to be fairly broken before - # switch to ELF - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - - freebsd-elf*) - _LT_TAGVAR(archive_cmds_need_lc, $1)=no - ;; - - freebsd* | dragonfly*) - # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF - # conventions - _LT_TAGVAR(ld_shlibs, $1)=yes - ;; - - gnu*) - ;; - - hpux9*) - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' - _LT_TAGVAR(hardcode_libdir_separator, $1)=: - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, - # but as the default - # location of the library. - - case $cc_basename in - CC*) - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - aCC*) - _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' - ;; - *) - if test "$GXX" = yes; then - _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - else - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - fi - ;; - esac - ;; - - hpux10*|hpux11*) - if test $with_gnu_ld = no; then - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' - _LT_TAGVAR(hardcode_libdir_separator, $1)=: - - case $host_cpu in - hppa*64*|ia64*) - ;; - *) - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - ;; - esac - fi - case $host_cpu in - hppa*64*|ia64*) - _LT_TAGVAR(hardcode_direct, $1)=no - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - *) - _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_direct_absolute, $1)=yes - _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, - # but as the default - # location of the library. - ;; - esac - - case $cc_basename in - CC*) - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - aCC*) - case $host_cpu in - hppa*64*) - _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - ia64*) - _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - *) - _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - esac - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' - ;; - *) - if test "$GXX" = yes; then - if test $with_gnu_ld = no; then - case $host_cpu in - hppa*64*) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - ia64*) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - *) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - esac - fi - else - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - fi - ;; - esac - ;; - - interix[[3-9]]*) - _LT_TAGVAR(hardcode_direct, $1)=no - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. - # Instead, shared libraries are loaded at an image base (0x10000000 by - # default) and relocated if they conflict, which is a slow very memory - # consuming and fragmenting process. To avoid this, we pick a random, - # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link - # time. Moving up from 0x10000000 also allows more sbrk(2) space. - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - ;; - irix5* | irix6*) - case $cc_basename in - CC*) - # SGI C++ - _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' - - # Archives containing C++ object files must be created using - # "CC -ar", where "CC" is the IRIX C++ compiler. This is - # necessary to make sure instantiated templates are included - # in the archive. - _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' - ;; - *) - if test "$GXX" = yes; then - if test "$with_gnu_ld" = no; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - else - _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` -o $lib' - fi - fi - _LT_TAGVAR(link_all_deplibs, $1)=yes - ;; - esac - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_TAGVAR(hardcode_libdir_separator, $1)=: - _LT_TAGVAR(inherit_rpath, $1)=yes - ;; - - linux* | k*bsd*-gnu | kopensolaris*-gnu) - case $cc_basename in - KCC*) - # Kuck and Associates, Inc. (KAI) C++ Compiler - - # KCC will only create a shared library if the output file - # ends with ".so" (or ".sl" for HP-UX), so rename the library - # to its proper name (with version) after linking. - _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib' - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' - - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' - - # Archives containing C++ object files must be created using - # "CC -Bstatic", where "CC" is the KAI C++ compiler. - _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' - ;; - icpc* | ecpc* ) - # Intel C++ - with_gnu_ld=yes - # version 8.0 and above of icpc choke on multiply defined symbols - # if we add $predep_objects and $postdep_objects, however 7.1 and - # earlier do not add the objects themselves. - case `$CC -V 2>&1` in - *"Version 7."*) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - ;; - *) # Version 8.0 or newer - tmp_idyn= - case $host_cpu in - ia64*) tmp_idyn=' -i_dynamic';; - esac - _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - ;; - esac - _LT_TAGVAR(archive_cmds_need_lc, $1)=no - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' - _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' - ;; - pgCC* | pgcpp*) - # Portland Group C++ compiler - case `$CC -V` in - *pgCC\ [[1-5]]* | *pgcpp\ [[1-5]]*) - _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~ - rm -rf $tpldir~ - $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ - compile_command="$compile_command `find $tpldir -name \*.o | $NL2SP`"' - _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~ - rm -rf $tpldir~ - $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ - $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | $NL2SP`~ - $RANLIB $oldlib' - _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~ - rm -rf $tpldir~ - $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ - $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~ - rm -rf $tpldir~ - $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ - $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' - ;; - *) # Version 6 will use weak symbols - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' - ;; - esac - - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' - _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' - ;; - cxx*) - # Compaq C++ - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols' - - runpath_var=LD_RUN_PATH - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' - _LT_TAGVAR(hardcode_libdir_separator, $1)=: - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`$ECHO "X$templist" | $Xsed -e "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' - ;; - xl*) - # IBM XL 8.0 on PPC, with GNU ld - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' - _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - if test "x$supports_anon_versioning" = xyes; then - _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ - cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ - echo "local: *; };" >> $output_objdir/$libname.ver~ - $CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' - fi - ;; - *) - case `$CC -V 2>&1 | sed 5q` in - *Sun\ C*) - # Sun C++ 5.9 - _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' - _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' - _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' - _LT_TAGVAR(compiler_needs_object, $1)=yes - - # Not sure whether something based on - # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 - # would be better. - output_verbose_link_cmd='echo' - - # Archives containing C++ object files must be created using - # "CC -xar", where "CC" is the Sun C++ compiler. This is - # necessary to make sure instantiated templates are included - # in the archive. - _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' - ;; - esac - ;; - esac - ;; - - lynxos*) - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - - m88k*) - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - - mvs*) - case $cc_basename in - cxx*) - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - *) - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - esac - ;; - - netbsd*) - if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then - _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' - wlarc= - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' - _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - fi - # Workaround some broken pre-1.5 toolchains - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' - ;; - - *nto* | *qnx*) - _LT_TAGVAR(ld_shlibs, $1)=yes - ;; - - openbsd2*) - # C++ shared libraries are fairly broken - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - - openbsd*) - if test -f /usr/libexec/ld.so; then - _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - _LT_TAGVAR(hardcode_direct_absolute, $1)=yes - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' - fi - output_verbose_link_cmd=echo - else - _LT_TAGVAR(ld_shlibs, $1)=no - fi - ;; - - osf3* | osf4* | osf5*) - case $cc_basename in - KCC*) - # Kuck and Associates, Inc. (KAI) C++ Compiler - - # KCC will only create a shared library if the output file - # ends with ".so" (or ".sl" for HP-UX), so rename the library - # to its proper name (with version) after linking. - _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' - - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - _LT_TAGVAR(hardcode_libdir_separator, $1)=: - - # Archives containing C++ object files must be created using - # the KAI C++ compiler. - case $host in - osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; - *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;; - esac - ;; - RCC*) - # Rational C++ 2.4.1 - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - cxx*) - case $host in - osf3*) - _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' - _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && $ECHO "X${wl}-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - ;; - *) - _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' - _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ - echo "-hidden">> $lib.exp~ - $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~ - $RM $lib.exp' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' - ;; - esac - - _LT_TAGVAR(hardcode_libdir_separator, $1)=: - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`$ECHO "X$templist" | $Xsed -e "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' - ;; - *) - if test "$GXX" = yes && test "$with_gnu_ld" = no; then - _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' - case $host in - osf3*) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - ;; - *) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - ;; - esac - - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_TAGVAR(hardcode_libdir_separator, $1)=: - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"' - - else - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - fi - ;; - esac - ;; - - psos*) - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - - sunos4*) - case $cc_basename in - CC*) - # Sun C++ 4.x - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - lcc*) - # Lucid - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - *) - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - esac - ;; - - solaris*) - case $cc_basename in - CC*) - # Sun C++ 4.2, 5.x and Centerline C++ - _LT_TAGVAR(archive_cmds_need_lc,$1)=yes - _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' - _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ - $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' - - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - case $host_os in - solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; - *) - # The compiler driver will combine and reorder linker options, - # but understands `-z linker_flag'. - # Supported since Solaris 2.6 (maybe 2.5.1?) - _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' - ;; - esac - _LT_TAGVAR(link_all_deplibs, $1)=yes - - output_verbose_link_cmd='echo' - - # Archives containing C++ object files must be created using - # "CC -xar", where "CC" is the Sun C++ compiler. This is - # necessary to make sure instantiated templates are included - # in the archive. - _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' - ;; - gcx*) - # Green Hills C++ Compiler - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' - - # The C++ compiler must be used to create the archive. - _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' - ;; - *) - # GNU C++ compiler with Solaris linker - if test "$GXX" = yes && test "$with_gnu_ld" = no; then - _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-z ${wl}defs' - if $CC --version | $GREP -v '^2\.7' > /dev/null; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ - $CC -shared -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"' - else - # g++ 2.7 appears to require `-G' NOT `-shared' on this - # platform. - _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ - $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"' - fi - - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $wl$libdir' - case $host_os in - solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; - *) - _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' - ;; - esac - fi - ;; - esac - ;; - - sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) - _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' - _LT_TAGVAR(archive_cmds_need_lc, $1)=no - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - runpath_var='LD_RUN_PATH' - - case $cc_basename in - CC*) - _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - ;; - - sysv5* | sco3.2v5* | sco5v6*) - # Note: We can NOT use -z defs as we might desire, because we do not - # link with -lc, and that would cause any symbols used from libc to - # always be unresolved, which means just about no library would - # ever link correctly. If we're not using GNU ld we use -z text - # though, which does catch some bad symbols but isn't as heavy-handed - # as -z defs. - _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' - _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' - _LT_TAGVAR(archive_cmds_need_lc, $1)=no - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir' - _LT_TAGVAR(hardcode_libdir_separator, $1)=':' - _LT_TAGVAR(link_all_deplibs, $1)=yes - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' - runpath_var='LD_RUN_PATH' - - case $cc_basename in - CC*) - _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - ;; - - tandem*) - case $cc_basename in - NCC*) - # NonStop-UX NCC 3.20 - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - *) - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - esac - ;; - - vxworks*) - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - - *) - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - esac - - AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) - test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no - - _LT_TAGVAR(GCC, $1)="$GXX" - _LT_TAGVAR(LD, $1)="$LD" - - ## CAVEAT EMPTOR: - ## There is no encapsulation within the following macros, do not change - ## the running order or otherwise move them around unless you know exactly - ## what you are doing... - _LT_SYS_HIDDEN_LIBDEPS($1) - _LT_COMPILER_PIC($1) - _LT_COMPILER_C_O($1) - _LT_COMPILER_FILE_LOCKS($1) - _LT_LINKER_SHLIBS($1) - _LT_SYS_DYNAMIC_LINKER($1) - _LT_LINKER_HARDCODE_LIBPATH($1) - - _LT_CONFIG($1) - fi # test -n "$compiler" - - CC=$lt_save_CC - LDCXX=$LD - LD=$lt_save_LD - GCC=$lt_save_GCC - with_gnu_ld=$lt_save_with_gnu_ld - lt_cv_path_LDCXX=$lt_cv_path_LD - lt_cv_path_LD=$lt_save_path_LD - lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld - lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld -fi # test "$_lt_caught_CXX_error" != yes - -AC_LANG_POP -])# _LT_LANG_CXX_CONFIG - - -# _LT_SYS_HIDDEN_LIBDEPS([TAGNAME]) -# --------------------------------- -# Figure out "hidden" library dependencies from verbose -# compiler output when linking a shared library. -# Parse the compiler output and extract the necessary -# objects, libraries and library flags. -m4_defun([_LT_SYS_HIDDEN_LIBDEPS], -[m4_require([_LT_FILEUTILS_DEFAULTS])dnl -# Dependencies to place before and after the object being linked: -_LT_TAGVAR(predep_objects, $1)= -_LT_TAGVAR(postdep_objects, $1)= -_LT_TAGVAR(predeps, $1)= -_LT_TAGVAR(postdeps, $1)= -_LT_TAGVAR(compiler_lib_search_path, $1)= - -dnl we can't use the lt_simple_compile_test_code here, -dnl because it contains code intended for an executable, -dnl not a library. It's possible we should let each -dnl tag define a new lt_????_link_test_code variable, -dnl but it's only used here... -m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF -int a; -void foo (void) { a = 0; } -_LT_EOF -], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF -class Foo -{ -public: - Foo (void) { a = 0; } -private: - int a; -}; -_LT_EOF -], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF - subroutine foo - implicit none - integer*4 a - a=0 - return - end -_LT_EOF -], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF - subroutine foo - implicit none - integer a - a=0 - return - end -_LT_EOF -], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF -public class foo { - private int a; - public void bar (void) { - a = 0; - } -}; -_LT_EOF -]) -dnl Parse the compiler output and extract the necessary -dnl objects, libraries and library flags. -if AC_TRY_EVAL(ac_compile); then - # Parse the compiler output and extract the necessary - # objects, libraries and library flags. - - # Sentinel used to keep track of whether or not we are before - # the conftest object file. - pre_test_object_deps_done=no - - for p in `eval "$output_verbose_link_cmd"`; do - case $p in - - -L* | -R* | -l*) - # Some compilers place space between "-{L,R}" and the path. - # Remove the space. - if test $p = "-L" || - test $p = "-R"; then - prev=$p - continue - else - prev= - fi - - if test "$pre_test_object_deps_done" = no; then - case $p in - -L* | -R*) - # Internal compiler library paths should come after those - # provided the user. The postdeps already come after the - # user supplied libs so there is no need to process them. - if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then - _LT_TAGVAR(compiler_lib_search_path, $1)="${prev}${p}" - else - _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} ${prev}${p}" - fi - ;; - # The "-l" case would never come before the object being - # linked, so don't bother handling this case. - esac - else - if test -z "$_LT_TAGVAR(postdeps, $1)"; then - _LT_TAGVAR(postdeps, $1)="${prev}${p}" - else - _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} ${prev}${p}" - fi - fi - ;; - - *.$objext) - # This assumes that the test object file only shows up - # once in the compiler output. - if test "$p" = "conftest.$objext"; then - pre_test_object_deps_done=yes - continue - fi - - if test "$pre_test_object_deps_done" = no; then - if test -z "$_LT_TAGVAR(predep_objects, $1)"; then - _LT_TAGVAR(predep_objects, $1)="$p" - else - _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p" - fi - else - if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then - _LT_TAGVAR(postdep_objects, $1)="$p" - else - _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p" - fi - fi - ;; - - *) ;; # Ignore the rest. - - esac - done - - # Clean up. - rm -f a.out a.exe -else - echo "libtool.m4: error: problem compiling $1 test program" -fi - -$RM -f confest.$objext - -# PORTME: override above test on systems where it is broken -m4_if([$1], [CXX], -[case $host_os in -interix[[3-9]]*) - # Interix 3.5 installs completely hosed .la files for C++, so rather than - # hack all around it, let's just trust "g++" to DTRT. - _LT_TAGVAR(predep_objects,$1)= - _LT_TAGVAR(postdep_objects,$1)= - _LT_TAGVAR(postdeps,$1)= - ;; - -linux*) - case `$CC -V 2>&1 | sed 5q` in - *Sun\ C*) - # Sun C++ 5.9 - - # The more standards-conforming stlport4 library is - # incompatible with the Cstd library. Avoid specifying - # it if it's in CXXFLAGS. Ignore libCrun as - # -library=stlport4 depends on it. - case " $CXX $CXXFLAGS " in - *" -library=stlport4 "*) - solaris_use_stlport4=yes - ;; - esac - - if test "$solaris_use_stlport4" != yes; then - _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun' - fi - ;; - esac - ;; - -solaris*) - case $cc_basename in - CC*) - # The more standards-conforming stlport4 library is - # incompatible with the Cstd library. Avoid specifying - # it if it's in CXXFLAGS. Ignore libCrun as - # -library=stlport4 depends on it. - case " $CXX $CXXFLAGS " in - *" -library=stlport4 "*) - solaris_use_stlport4=yes - ;; - esac - - # Adding this requires a known-good setup of shared libraries for - # Sun compiler versions before 5.6, else PIC objects from an old - # archive will be linked into the output, leading to subtle bugs. - if test "$solaris_use_stlport4" != yes; then - _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun' - fi - ;; - esac - ;; -esac -]) - -case " $_LT_TAGVAR(postdeps, $1) " in -*" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; -esac - _LT_TAGVAR(compiler_lib_search_dirs, $1)= -if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then - _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | ${SED} -e 's! -L! !g' -e 's!^ !!'` -fi -_LT_TAGDECL([], [compiler_lib_search_dirs], [1], - [The directories searched by this compiler when creating a shared library]) -_LT_TAGDECL([], [predep_objects], [1], - [Dependencies to place before and after the objects being linked to - create a shared library]) -_LT_TAGDECL([], [postdep_objects], [1]) -_LT_TAGDECL([], [predeps], [1]) -_LT_TAGDECL([], [postdeps], [1]) -_LT_TAGDECL([], [compiler_lib_search_path], [1], - [The library search path used internally by the compiler when linking - a shared library]) -])# _LT_SYS_HIDDEN_LIBDEPS - - -# _LT_PROG_F77 -# ------------ -# Since AC_PROG_F77 is broken, in that it returns the empty string -# if there is no fortran compiler, we have our own version here. -m4_defun([_LT_PROG_F77], -[ -pushdef([AC_MSG_ERROR], [_lt_disable_F77=yes]) -AC_PROG_F77 -if test -z "$F77" || test "X$F77" = "Xno"; then - _lt_disable_F77=yes -fi -popdef([AC_MSG_ERROR]) -])# _LT_PROG_F77 - -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([_LT_PROG_F77], []) - - -# _LT_LANG_F77_CONFIG([TAG]) -# -------------------------- -# Ensure that the configuration variables for a Fortran 77 compiler are -# suitably defined. These variables are subsequently used by _LT_CONFIG -# to write the compiler configuration to `libtool'. -m4_defun([_LT_LANG_F77_CONFIG], -[AC_REQUIRE([_LT_PROG_F77])dnl -AC_LANG_PUSH(Fortran 77) - -_LT_TAGVAR(archive_cmds_need_lc, $1)=no -_LT_TAGVAR(allow_undefined_flag, $1)= -_LT_TAGVAR(always_export_symbols, $1)=no -_LT_TAGVAR(archive_expsym_cmds, $1)= -_LT_TAGVAR(export_dynamic_flag_spec, $1)= -_LT_TAGVAR(hardcode_direct, $1)=no -_LT_TAGVAR(hardcode_direct_absolute, $1)=no -_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= -_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= -_LT_TAGVAR(hardcode_libdir_separator, $1)= -_LT_TAGVAR(hardcode_minus_L, $1)=no -_LT_TAGVAR(hardcode_automatic, $1)=no -_LT_TAGVAR(inherit_rpath, $1)=no -_LT_TAGVAR(module_cmds, $1)= -_LT_TAGVAR(module_expsym_cmds, $1)= -_LT_TAGVAR(link_all_deplibs, $1)=unknown -_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds -_LT_TAGVAR(no_undefined_flag, $1)= -_LT_TAGVAR(whole_archive_flag_spec, $1)= -_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no - -# Source file extension for f77 test sources. -ac_ext=f - -# Object file extension for compiled f77 test sources. -objext=o -_LT_TAGVAR(objext, $1)=$objext - -# No sense in running all these tests if we already determined that -# the F77 compiler isn't working. Some variables (like enable_shared) -# are currently assumed to apply to all compilers on this platform, -# and will be corrupted by setting them based on a non-working compiler. -if test "$_lt_disable_F77" != yes; then - # Code to be used in simple compile tests - lt_simple_compile_test_code="\ - subroutine t - return - end -" - - # Code to be used in simple link tests - lt_simple_link_test_code="\ - program t - end -" - - # ltmain only uses $CC for tagged configurations so make sure $CC is set. - _LT_TAG_COMPILER - - # save warnings/boilerplate of simple test code - _LT_COMPILER_BOILERPLATE - _LT_LINKER_BOILERPLATE - - # Allow CC to be a program name with arguments. - lt_save_CC="$CC" - lt_save_GCC=$GCC - CC=${F77-"f77"} - compiler=$CC - _LT_TAGVAR(compiler, $1)=$CC - _LT_CC_BASENAME([$compiler]) - GCC=$G77 - if test -n "$compiler"; then - AC_MSG_CHECKING([if libtool supports shared libraries]) - AC_MSG_RESULT([$can_build_shared]) - - AC_MSG_CHECKING([whether to build shared libraries]) - test "$can_build_shared" = "no" && enable_shared=no - - # On AIX, shared libraries and static libraries use the same namespace, and - # are all built from PIC. - case $host_os in - aix3*) - test "$enable_shared" = yes && enable_static=no - if test -n "$RANLIB"; then - archive_cmds="$archive_cmds~\$RANLIB \$lib" - postinstall_cmds='$RANLIB $lib' - fi - ;; - aix[[4-9]]*) - if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then - test "$enable_shared" = yes && enable_static=no - fi - ;; - esac - AC_MSG_RESULT([$enable_shared]) - - AC_MSG_CHECKING([whether to build static libraries]) - # Make sure either enable_shared or enable_static is yes. - test "$enable_shared" = yes || enable_static=yes - AC_MSG_RESULT([$enable_static]) - - _LT_TAGVAR(GCC, $1)="$G77" - _LT_TAGVAR(LD, $1)="$LD" - - ## CAVEAT EMPTOR: - ## There is no encapsulation within the following macros, do not change - ## the running order or otherwise move them around unless you know exactly - ## what you are doing... - _LT_COMPILER_PIC($1) - _LT_COMPILER_C_O($1) - _LT_COMPILER_FILE_LOCKS($1) - _LT_LINKER_SHLIBS($1) - _LT_SYS_DYNAMIC_LINKER($1) - _LT_LINKER_HARDCODE_LIBPATH($1) - - _LT_CONFIG($1) - fi # test -n "$compiler" - - GCC=$lt_save_GCC - CC="$lt_save_CC" -fi # test "$_lt_disable_F77" != yes - -AC_LANG_POP -])# _LT_LANG_F77_CONFIG - - -# _LT_PROG_FC -# ----------- -# Since AC_PROG_FC is broken, in that it returns the empty string -# if there is no fortran compiler, we have our own version here. -m4_defun([_LT_PROG_FC], -[ -pushdef([AC_MSG_ERROR], [_lt_disable_FC=yes]) -AC_PROG_FC -if test -z "$FC" || test "X$FC" = "Xno"; then - _lt_disable_FC=yes -fi -popdef([AC_MSG_ERROR]) -])# _LT_PROG_FC - -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([_LT_PROG_FC], []) - - -# _LT_LANG_FC_CONFIG([TAG]) -# ------------------------- -# Ensure that the configuration variables for a Fortran compiler are -# suitably defined. These variables are subsequently used by _LT_CONFIG -# to write the compiler configuration to `libtool'. -m4_defun([_LT_LANG_FC_CONFIG], -[AC_REQUIRE([_LT_PROG_FC])dnl -AC_LANG_PUSH(Fortran) - -_LT_TAGVAR(archive_cmds_need_lc, $1)=no -_LT_TAGVAR(allow_undefined_flag, $1)= -_LT_TAGVAR(always_export_symbols, $1)=no -_LT_TAGVAR(archive_expsym_cmds, $1)= -_LT_TAGVAR(export_dynamic_flag_spec, $1)= -_LT_TAGVAR(hardcode_direct, $1)=no -_LT_TAGVAR(hardcode_direct_absolute, $1)=no -_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= -_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= -_LT_TAGVAR(hardcode_libdir_separator, $1)= -_LT_TAGVAR(hardcode_minus_L, $1)=no -_LT_TAGVAR(hardcode_automatic, $1)=no -_LT_TAGVAR(inherit_rpath, $1)=no -_LT_TAGVAR(module_cmds, $1)= -_LT_TAGVAR(module_expsym_cmds, $1)= -_LT_TAGVAR(link_all_deplibs, $1)=unknown -_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds -_LT_TAGVAR(no_undefined_flag, $1)= -_LT_TAGVAR(whole_archive_flag_spec, $1)= -_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no - -# Source file extension for fc test sources. -ac_ext=${ac_fc_srcext-f} - -# Object file extension for compiled fc test sources. -objext=o -_LT_TAGVAR(objext, $1)=$objext - -# No sense in running all these tests if we already determined that -# the FC compiler isn't working. Some variables (like enable_shared) -# are currently assumed to apply to all compilers on this platform, -# and will be corrupted by setting them based on a non-working compiler. -if test "$_lt_disable_FC" != yes; then - # Code to be used in simple compile tests - lt_simple_compile_test_code="\ - subroutine t - return - end -" - - # Code to be used in simple link tests - lt_simple_link_test_code="\ - program t - end -" - - # ltmain only uses $CC for tagged configurations so make sure $CC is set. - _LT_TAG_COMPILER - - # save warnings/boilerplate of simple test code - _LT_COMPILER_BOILERPLATE - _LT_LINKER_BOILERPLATE - - # Allow CC to be a program name with arguments. - lt_save_CC="$CC" - lt_save_GCC=$GCC - CC=${FC-"f95"} - compiler=$CC - GCC=$ac_cv_fc_compiler_gnu - - _LT_TAGVAR(compiler, $1)=$CC - _LT_CC_BASENAME([$compiler]) - - if test -n "$compiler"; then - AC_MSG_CHECKING([if libtool supports shared libraries]) - AC_MSG_RESULT([$can_build_shared]) - - AC_MSG_CHECKING([whether to build shared libraries]) - test "$can_build_shared" = "no" && enable_shared=no - - # On AIX, shared libraries and static libraries use the same namespace, and - # are all built from PIC. - case $host_os in - aix3*) - test "$enable_shared" = yes && enable_static=no - if test -n "$RANLIB"; then - archive_cmds="$archive_cmds~\$RANLIB \$lib" - postinstall_cmds='$RANLIB $lib' - fi - ;; - aix[[4-9]]*) - if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then - test "$enable_shared" = yes && enable_static=no - fi - ;; - esac - AC_MSG_RESULT([$enable_shared]) - - AC_MSG_CHECKING([whether to build static libraries]) - # Make sure either enable_shared or enable_static is yes. - test "$enable_shared" = yes || enable_static=yes - AC_MSG_RESULT([$enable_static]) - - _LT_TAGVAR(GCC, $1)="$ac_cv_fc_compiler_gnu" - _LT_TAGVAR(LD, $1)="$LD" - - ## CAVEAT EMPTOR: - ## There is no encapsulation within the following macros, do not change - ## the running order or otherwise move them around unless you know exactly - ## what you are doing... - _LT_SYS_HIDDEN_LIBDEPS($1) - _LT_COMPILER_PIC($1) - _LT_COMPILER_C_O($1) - _LT_COMPILER_FILE_LOCKS($1) - _LT_LINKER_SHLIBS($1) - _LT_SYS_DYNAMIC_LINKER($1) - _LT_LINKER_HARDCODE_LIBPATH($1) - - _LT_CONFIG($1) - fi # test -n "$compiler" - - GCC=$lt_save_GCC - CC="$lt_save_CC" -fi # test "$_lt_disable_FC" != yes - -AC_LANG_POP -])# _LT_LANG_FC_CONFIG - - -# _LT_LANG_GCJ_CONFIG([TAG]) -# -------------------------- -# Ensure that the configuration variables for the GNU Java Compiler compiler -# are suitably defined. These variables are subsequently used by _LT_CONFIG -# to write the compiler configuration to `libtool'. -m4_defun([_LT_LANG_GCJ_CONFIG], -[AC_REQUIRE([LT_PROG_GCJ])dnl -AC_LANG_SAVE - -# Source file extension for Java test sources. -ac_ext=java - -# Object file extension for compiled Java test sources. -objext=o -_LT_TAGVAR(objext, $1)=$objext - -# Code to be used in simple compile tests -lt_simple_compile_test_code="class foo {}" - -# Code to be used in simple link tests -lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }' - -# ltmain only uses $CC for tagged configurations so make sure $CC is set. -_LT_TAG_COMPILER - -# save warnings/boilerplate of simple test code -_LT_COMPILER_BOILERPLATE -_LT_LINKER_BOILERPLATE - -# Allow CC to be a program name with arguments. -lt_save_CC="$CC" -lt_save_GCC=$GCC -GCC=yes -CC=${GCJ-"gcj"} -compiler=$CC -_LT_TAGVAR(compiler, $1)=$CC -_LT_TAGVAR(LD, $1)="$LD" -_LT_CC_BASENAME([$compiler]) - -# GCJ did not exist at the time GCC didn't implicitly link libc in. -_LT_TAGVAR(archive_cmds_need_lc, $1)=no - -_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds - -## CAVEAT EMPTOR: -## There is no encapsulation within the following macros, do not change -## the running order or otherwise move them around unless you know exactly -## what you are doing... -if test -n "$compiler"; then - _LT_COMPILER_NO_RTTI($1) - _LT_COMPILER_PIC($1) - _LT_COMPILER_C_O($1) - _LT_COMPILER_FILE_LOCKS($1) - _LT_LINKER_SHLIBS($1) - _LT_LINKER_HARDCODE_LIBPATH($1) - - _LT_CONFIG($1) -fi - -AC_LANG_RESTORE - -GCC=$lt_save_GCC -CC="$lt_save_CC" -])# _LT_LANG_GCJ_CONFIG - - -# _LT_LANG_RC_CONFIG([TAG]) -# ------------------------- -# Ensure that the configuration variables for the Windows resource compiler -# are suitably defined. These variables are subsequently used by _LT_CONFIG -# to write the compiler configuration to `libtool'. -m4_defun([_LT_LANG_RC_CONFIG], -[AC_REQUIRE([LT_PROG_RC])dnl -AC_LANG_SAVE - -# Source file extension for RC test sources. -ac_ext=rc - -# Object file extension for compiled RC test sources. -objext=o -_LT_TAGVAR(objext, $1)=$objext - -# Code to be used in simple compile tests -lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }' - -# Code to be used in simple link tests -lt_simple_link_test_code="$lt_simple_compile_test_code" - -# ltmain only uses $CC for tagged configurations so make sure $CC is set. -_LT_TAG_COMPILER - -# save warnings/boilerplate of simple test code -_LT_COMPILER_BOILERPLATE -_LT_LINKER_BOILERPLATE - -# Allow CC to be a program name with arguments. -lt_save_CC="$CC" -lt_save_GCC=$GCC -GCC= -CC=${RC-"windres"} -compiler=$CC -_LT_TAGVAR(compiler, $1)=$CC -_LT_CC_BASENAME([$compiler]) -_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes - -if test -n "$compiler"; then - : - _LT_CONFIG($1) -fi - -GCC=$lt_save_GCC -AC_LANG_RESTORE -CC="$lt_save_CC" -])# _LT_LANG_RC_CONFIG - - -# LT_PROG_GCJ -# ----------- -AC_DEFUN([LT_PROG_GCJ], -[m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ], - [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ], - [AC_CHECK_TOOL(GCJ, gcj,) - test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2" - AC_SUBST(GCJFLAGS)])])[]dnl -]) - -# Old name: -AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ]) -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([LT_AC_PROG_GCJ], []) - - -# LT_PROG_RC -# ---------- -AC_DEFUN([LT_PROG_RC], -[AC_CHECK_TOOL(RC, windres,) -]) - -# Old name: -AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC]) -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([LT_AC_PROG_RC], []) - - -# _LT_DECL_EGREP -# -------------- -# If we don't have a new enough Autoconf to choose the best grep -# available, choose the one first in the user's PATH. -m4_defun([_LT_DECL_EGREP], -[AC_REQUIRE([AC_PROG_EGREP])dnl -AC_REQUIRE([AC_PROG_FGREP])dnl -test -z "$GREP" && GREP=grep -_LT_DECL([], [GREP], [1], [A grep program that handles long lines]) -_LT_DECL([], [EGREP], [1], [An ERE matcher]) -_LT_DECL([], [FGREP], [1], [A literal string matcher]) -dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too -AC_SUBST([GREP]) -]) - - -# _LT_DECL_OBJDUMP -# -------------- -# If we don't have a new enough Autoconf to choose the best objdump -# available, choose the one first in the user's PATH. -m4_defun([_LT_DECL_OBJDUMP], -[AC_CHECK_TOOL(OBJDUMP, objdump, false) -test -z "$OBJDUMP" && OBJDUMP=objdump -_LT_DECL([], [OBJDUMP], [1], [An object symbol dumper]) -AC_SUBST([OBJDUMP]) -]) - - -# _LT_DECL_SED -# ------------ -# Check for a fully-functional sed program, that truncates -# as few characters as possible. Prefer GNU sed if found. -m4_defun([_LT_DECL_SED], -[AC_PROG_SED -test -z "$SED" && SED=sed -Xsed="$SED -e 1s/^X//" -_LT_DECL([], [SED], [1], [A sed program that does not truncate output]) -_LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"], - [Sed that helps us avoid accidentally triggering echo(1) options like -n]) -])# _LT_DECL_SED - -m4_ifndef([AC_PROG_SED], [ -############################################################ -# NOTE: This macro has been submitted for inclusion into # -# GNU Autoconf as AC_PROG_SED. When it is available in # -# a released version of Autoconf we should remove this # -# macro and use it instead. # -############################################################ - -m4_defun([AC_PROG_SED], -[AC_MSG_CHECKING([for a sed that does not truncate output]) -AC_CACHE_VAL(lt_cv_path_SED, -[# Loop through the user's path and test for sed and gsed. -# Then use that list of sed's as ones to test for truncation. -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for lt_ac_prog in sed gsed; do - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then - lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" - fi - done - done -done -IFS=$as_save_IFS -lt_ac_max=0 -lt_ac_count=0 -# Add /usr/xpg4/bin/sed as it is typically found on Solaris -# along with /bin/sed that truncates output. -for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do - test ! -f $lt_ac_sed && continue - cat /dev/null > conftest.in - lt_ac_count=0 - echo $ECHO_N "0123456789$ECHO_C" >conftest.in - # Check for GNU sed and select it if it is found. - if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then - lt_cv_path_SED=$lt_ac_sed - break - fi - while true; do - cat conftest.in conftest.in >conftest.tmp - mv conftest.tmp conftest.in - cp conftest.in conftest.nl - echo >>conftest.nl - $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break - cmp -s conftest.out conftest.nl || break - # 10000 chars as input seems more than enough - test $lt_ac_count -gt 10 && break - lt_ac_count=`expr $lt_ac_count + 1` - if test $lt_ac_count -gt $lt_ac_max; then - lt_ac_max=$lt_ac_count - lt_cv_path_SED=$lt_ac_sed - fi - done -done -]) -SED=$lt_cv_path_SED -AC_SUBST([SED]) -AC_MSG_RESULT([$SED]) -])#AC_PROG_SED -])#m4_ifndef - -# Old name: -AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED]) -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([LT_AC_PROG_SED], []) - - -# _LT_CHECK_SHELL_FEATURES -# ------------------------ -# Find out whether the shell is Bourne or XSI compatible, -# or has some other useful features. -m4_defun([_LT_CHECK_SHELL_FEATURES], -[AC_MSG_CHECKING([whether the shell understands some XSI constructs]) -# Try some XSI features -xsi_shell=no -( _lt_dummy="a/b/c" - test "${_lt_dummy##*/},${_lt_dummy%/*},"${_lt_dummy%"$_lt_dummy"}, \ - = c,a/b,, \ - && eval 'test $(( 1 + 1 )) -eq 2 \ - && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \ - && xsi_shell=yes -AC_MSG_RESULT([$xsi_shell]) -_LT_CONFIG_LIBTOOL_INIT([xsi_shell='$xsi_shell']) - -AC_MSG_CHECKING([whether the shell understands "+="]) -lt_shell_append=no -( foo=bar; set foo baz; eval "$[1]+=\$[2]" && test "$foo" = barbaz ) \ - >/dev/null 2>&1 \ - && lt_shell_append=yes -AC_MSG_RESULT([$lt_shell_append]) -_LT_CONFIG_LIBTOOL_INIT([lt_shell_append='$lt_shell_append']) - -if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then - lt_unset=unset -else - lt_unset=false -fi -_LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl - -# test EBCDIC or ASCII -case `echo X|tr X '\101'` in - A) # ASCII based system - # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr - lt_SP2NL='tr \040 \012' - lt_NL2SP='tr \015\012 \040\040' - ;; - *) # EBCDIC based system - lt_SP2NL='tr \100 \n' - lt_NL2SP='tr \r\n \100\100' - ;; -esac -_LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl -_LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl -])# _LT_CHECK_SHELL_FEATURES - - -# _LT_PROG_XSI_SHELLFNS -# --------------------- -# Bourne and XSI compatible variants of some useful shell functions. -m4_defun([_LT_PROG_XSI_SHELLFNS], -[case $xsi_shell in - yes) - cat << \_LT_EOF >> "$cfgfile" - -# func_dirname file append nondir_replacement -# Compute the dirname of FILE. If nonempty, add APPEND to the result, -# otherwise set result to NONDIR_REPLACEMENT. -func_dirname () -{ - case ${1} in - */*) func_dirname_result="${1%/*}${2}" ;; - * ) func_dirname_result="${3}" ;; - esac -} - -# func_basename file -func_basename () -{ - func_basename_result="${1##*/}" -} - -# func_dirname_and_basename file append nondir_replacement -# perform func_basename and func_dirname in a single function -# call: -# dirname: Compute the dirname of FILE. If nonempty, -# add APPEND to the result, otherwise set result -# to NONDIR_REPLACEMENT. -# value returned in "$func_dirname_result" -# basename: Compute filename of FILE. -# value retuned in "$func_basename_result" -# Implementation must be kept synchronized with func_dirname -# and func_basename. For efficiency, we do not delegate to -# those functions but instead duplicate the functionality here. -func_dirname_and_basename () -{ - case ${1} in - */*) func_dirname_result="${1%/*}${2}" ;; - * ) func_dirname_result="${3}" ;; - esac - func_basename_result="${1##*/}" -} - -# func_stripname prefix suffix name -# strip PREFIX and SUFFIX off of NAME. -# PREFIX and SUFFIX must not contain globbing or regex special -# characters, hashes, percent signs, but SUFFIX may contain a leading -# dot (in which case that matches only a dot). -func_stripname () -{ - # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are - # positional parameters, so assign one to ordinary parameter first. - func_stripname_result=${3} - func_stripname_result=${func_stripname_result#"${1}"} - func_stripname_result=${func_stripname_result%"${2}"} -} - -# func_opt_split -func_opt_split () -{ - func_opt_split_opt=${1%%=*} - func_opt_split_arg=${1#*=} -} - -# func_lo2o object -func_lo2o () -{ - case ${1} in - *.lo) func_lo2o_result=${1%.lo}.${objext} ;; - *) func_lo2o_result=${1} ;; - esac -} - -# func_xform libobj-or-source -func_xform () -{ - func_xform_result=${1%.*}.lo -} - -# func_arith arithmetic-term... -func_arith () -{ - func_arith_result=$(( $[*] )) -} - -# func_len string -# STRING may not start with a hyphen. -func_len () -{ - func_len_result=${#1} -} - -_LT_EOF - ;; - *) # Bourne compatible functions. - cat << \_LT_EOF >> "$cfgfile" - -# func_dirname file append nondir_replacement -# Compute the dirname of FILE. If nonempty, add APPEND to the result, -# otherwise set result to NONDIR_REPLACEMENT. -func_dirname () -{ - # Extract subdirectory from the argument. - func_dirname_result=`$ECHO "X${1}" | $Xsed -e "$dirname"` - if test "X$func_dirname_result" = "X${1}"; then - func_dirname_result="${3}" - else - func_dirname_result="$func_dirname_result${2}" - fi -} - -# func_basename file -func_basename () -{ - func_basename_result=`$ECHO "X${1}" | $Xsed -e "$basename"` -} - -dnl func_dirname_and_basename -dnl A portable version of this function is already defined in general.m4sh -dnl so there is no need for it here. - -# func_stripname prefix suffix name -# strip PREFIX and SUFFIX off of NAME. -# PREFIX and SUFFIX must not contain globbing or regex special -# characters, hashes, percent signs, but SUFFIX may contain a leading -# dot (in which case that matches only a dot). -# func_strip_suffix prefix name -func_stripname () -{ - case ${2} in - .*) func_stripname_result=`$ECHO "X${3}" \ - | $Xsed -e "s%^${1}%%" -e "s%\\\\${2}\$%%"`;; - *) func_stripname_result=`$ECHO "X${3}" \ - | $Xsed -e "s%^${1}%%" -e "s%${2}\$%%"`;; - esac -} - -# sed scripts: -my_sed_long_opt='1s/^\(-[[^=]]*\)=.*/\1/;q' -my_sed_long_arg='1s/^-[[^=]]*=//' - -# func_opt_split -func_opt_split () -{ - func_opt_split_opt=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_opt"` - func_opt_split_arg=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_arg"` -} - -# func_lo2o object -func_lo2o () -{ - func_lo2o_result=`$ECHO "X${1}" | $Xsed -e "$lo2o"` -} - -# func_xform libobj-or-source -func_xform () -{ - func_xform_result=`$ECHO "X${1}" | $Xsed -e 's/\.[[^.]]*$/.lo/'` -} - -# func_arith arithmetic-term... -func_arith () -{ - func_arith_result=`expr "$[@]"` -} - -# func_len string -# STRING may not start with a hyphen. -func_len () -{ - func_len_result=`expr "$[1]" : ".*" 2>/dev/null || echo $max_cmd_len` -} - -_LT_EOF -esac - -case $lt_shell_append in - yes) - cat << \_LT_EOF >> "$cfgfile" - -# func_append var value -# Append VALUE to the end of shell variable VAR. -func_append () -{ - eval "$[1]+=\$[2]" -} -_LT_EOF - ;; - *) - cat << \_LT_EOF >> "$cfgfile" - -# func_append var value -# Append VALUE to the end of shell variable VAR. -func_append () -{ - eval "$[1]=\$$[1]\$[2]" -} - -_LT_EOF - ;; - esac -]) diff --git a/m4/ltoptions.m4 b/m4/ltoptions.m4 deleted file mode 100644 index 34151a3..0000000 --- a/m4/ltoptions.m4 +++ /dev/null @@ -1,368 +0,0 @@ -# Helper functions for option handling. -*- Autoconf -*- -# -# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc. -# Written by Gary V. Vaughan, 2004 -# -# This file 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. - -# serial 6 ltoptions.m4 - -# This is to help aclocal find these macros, as it can't see m4_define. -AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])]) - - -# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME) -# ------------------------------------------ -m4_define([_LT_MANGLE_OPTION], -[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])]) - - -# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME) -# --------------------------------------- -# Set option OPTION-NAME for macro MACRO-NAME, and if there is a -# matching handler defined, dispatch to it. Other OPTION-NAMEs are -# saved as a flag. -m4_define([_LT_SET_OPTION], -[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl -m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]), - _LT_MANGLE_DEFUN([$1], [$2]), - [m4_warning([Unknown $1 option `$2'])])[]dnl -]) - - -# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET]) -# ------------------------------------------------------------ -# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. -m4_define([_LT_IF_OPTION], -[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])]) - - -# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET) -# ------------------------------------------------------- -# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME -# are set. -m4_define([_LT_UNLESS_OPTIONS], -[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), - [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option), - [m4_define([$0_found])])])[]dnl -m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3 -])[]dnl -]) - - -# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST) -# ---------------------------------------- -# OPTION-LIST is a space-separated list of Libtool options associated -# with MACRO-NAME. If any OPTION has a matching handler declared with -# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about -# the unknown option and exit. -m4_defun([_LT_SET_OPTIONS], -[# Set options -m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), - [_LT_SET_OPTION([$1], _LT_Option)]) - -m4_if([$1],[LT_INIT],[ - dnl - dnl Simply set some default values (i.e off) if boolean options were not - dnl specified: - _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no - ]) - _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no - ]) - dnl - dnl If no reference was made to various pairs of opposing options, then - dnl we run the default mode handler for the pair. For example, if neither - dnl `shared' nor `disable-shared' was passed, we enable building of shared - dnl archives by default: - _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED]) - _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC]) - _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC]) - _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install], - [_LT_ENABLE_FAST_INSTALL]) - ]) -])# _LT_SET_OPTIONS - - -## --------------------------------- ## -## Macros to handle LT_INIT options. ## -## --------------------------------- ## - -# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME) -# ----------------------------------------- -m4_define([_LT_MANGLE_DEFUN], -[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])]) - - -# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE) -# ----------------------------------------------- -m4_define([LT_OPTION_DEFINE], -[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl -])# LT_OPTION_DEFINE - - -# dlopen -# ------ -LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes -]) - -AU_DEFUN([AC_LIBTOOL_DLOPEN], -[_LT_SET_OPTION([LT_INIT], [dlopen]) -AC_DIAGNOSE([obsolete], -[$0: Remove this warning and the call to _LT_SET_OPTION when you -put the `dlopen' option into LT_INIT's first parameter.]) -]) - -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], []) - - -# win32-dll -# --------- -# Declare package support for building win32 dll's. -LT_OPTION_DEFINE([LT_INIT], [win32-dll], -[enable_win32_dll=yes - -case $host in -*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-cegcc*) - AC_CHECK_TOOL(AS, as, false) - AC_CHECK_TOOL(DLLTOOL, dlltool, false) - AC_CHECK_TOOL(OBJDUMP, objdump, false) - ;; -esac - -test -z "$AS" && AS=as -_LT_DECL([], [AS], [0], [Assembler program])dnl - -test -z "$DLLTOOL" && DLLTOOL=dlltool -_LT_DECL([], [DLLTOOL], [0], [DLL creation program])dnl - -test -z "$OBJDUMP" && OBJDUMP=objdump -_LT_DECL([], [OBJDUMP], [0], [Object dumper program])dnl -])# win32-dll - -AU_DEFUN([AC_LIBTOOL_WIN32_DLL], -[AC_REQUIRE([AC_CANONICAL_HOST])dnl -_LT_SET_OPTION([LT_INIT], [win32-dll]) -AC_DIAGNOSE([obsolete], -[$0: Remove this warning and the call to _LT_SET_OPTION when you -put the `win32-dll' option into LT_INIT's first parameter.]) -]) - -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], []) - - -# _LT_ENABLE_SHARED([DEFAULT]) -# ---------------------------- -# implement the --enable-shared flag, and supports the `shared' and -# `disable-shared' LT_INIT options. -# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. -m4_define([_LT_ENABLE_SHARED], -[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl -AC_ARG_ENABLE([shared], - [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@], - [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])], - [p=${PACKAGE-default} - case $enableval in - yes) enable_shared=yes ;; - no) enable_shared=no ;; - *) - enable_shared=no - # Look at the argument we got. We use all the common list separators. - lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," - for pkg in $enableval; do - IFS="$lt_save_ifs" - if test "X$pkg" = "X$p"; then - enable_shared=yes - fi - done - IFS="$lt_save_ifs" - ;; - esac], - [enable_shared=]_LT_ENABLE_SHARED_DEFAULT) - - _LT_DECL([build_libtool_libs], [enable_shared], [0], - [Whether or not to build shared libraries]) -])# _LT_ENABLE_SHARED - -LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])]) -LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])]) - -# Old names: -AC_DEFUN([AC_ENABLE_SHARED], -[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared]) -]) - -AC_DEFUN([AC_DISABLE_SHARED], -[_LT_SET_OPTION([LT_INIT], [disable-shared]) -]) - -AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) -AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) - -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([AM_ENABLE_SHARED], []) -dnl AC_DEFUN([AM_DISABLE_SHARED], []) - - - -# _LT_ENABLE_STATIC([DEFAULT]) -# ---------------------------- -# implement the --enable-static flag, and support the `static' and -# `disable-static' LT_INIT options. -# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. -m4_define([_LT_ENABLE_STATIC], -[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl -AC_ARG_ENABLE([static], - [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@], - [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])], - [p=${PACKAGE-default} - case $enableval in - yes) enable_static=yes ;; - no) enable_static=no ;; - *) - enable_static=no - # Look at the argument we got. We use all the common list separators. - lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," - for pkg in $enableval; do - IFS="$lt_save_ifs" - if test "X$pkg" = "X$p"; then - enable_static=yes - fi - done - IFS="$lt_save_ifs" - ;; - esac], - [enable_static=]_LT_ENABLE_STATIC_DEFAULT) - - _LT_DECL([build_old_libs], [enable_static], [0], - [Whether or not to build static libraries]) -])# _LT_ENABLE_STATIC - -LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])]) -LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])]) - -# Old names: -AC_DEFUN([AC_ENABLE_STATIC], -[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static]) -]) - -AC_DEFUN([AC_DISABLE_STATIC], -[_LT_SET_OPTION([LT_INIT], [disable-static]) -]) - -AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) -AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) - -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([AM_ENABLE_STATIC], []) -dnl AC_DEFUN([AM_DISABLE_STATIC], []) - - - -# _LT_ENABLE_FAST_INSTALL([DEFAULT]) -# ---------------------------------- -# implement the --enable-fast-install flag, and support the `fast-install' -# and `disable-fast-install' LT_INIT options. -# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. -m4_define([_LT_ENABLE_FAST_INSTALL], -[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl -AC_ARG_ENABLE([fast-install], - [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], - [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], - [p=${PACKAGE-default} - case $enableval in - yes) enable_fast_install=yes ;; - no) enable_fast_install=no ;; - *) - enable_fast_install=no - # Look at the argument we got. We use all the common list separators. - lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," - for pkg in $enableval; do - IFS="$lt_save_ifs" - if test "X$pkg" = "X$p"; then - enable_fast_install=yes - fi - done - IFS="$lt_save_ifs" - ;; - esac], - [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT) - -_LT_DECL([fast_install], [enable_fast_install], [0], - [Whether or not to optimize for fast installation])dnl -])# _LT_ENABLE_FAST_INSTALL - -LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])]) -LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])]) - -# Old names: -AU_DEFUN([AC_ENABLE_FAST_INSTALL], -[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) -AC_DIAGNOSE([obsolete], -[$0: Remove this warning and the call to _LT_SET_OPTION when you put -the `fast-install' option into LT_INIT's first parameter.]) -]) - -AU_DEFUN([AC_DISABLE_FAST_INSTALL], -[_LT_SET_OPTION([LT_INIT], [disable-fast-install]) -AC_DIAGNOSE([obsolete], -[$0: Remove this warning and the call to _LT_SET_OPTION when you put -the `disable-fast-install' option into LT_INIT's first parameter.]) -]) - -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], []) -dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], []) - - -# _LT_WITH_PIC([MODE]) -# -------------------- -# implement the --with-pic flag, and support the `pic-only' and `no-pic' -# LT_INIT options. -# MODE is either `yes' or `no'. If omitted, it defaults to `both'. -m4_define([_LT_WITH_PIC], -[AC_ARG_WITH([pic], - [AS_HELP_STRING([--with-pic], - [try to use only PIC/non-PIC objects @<:@default=use both@:>@])], - [pic_mode="$withval"], - [pic_mode=default]) - -test -z "$pic_mode" && pic_mode=m4_default([$1], [default]) - -_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl -])# _LT_WITH_PIC - -LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])]) -LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])]) - -# Old name: -AU_DEFUN([AC_LIBTOOL_PICMODE], -[_LT_SET_OPTION([LT_INIT], [pic-only]) -AC_DIAGNOSE([obsolete], -[$0: Remove this warning and the call to _LT_SET_OPTION when you -put the `pic-only' option into LT_INIT's first parameter.]) -]) - -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([AC_LIBTOOL_PICMODE], []) - -## ----------------- ## -## LTDL_INIT Options ## -## ----------------- ## - -m4_define([_LTDL_MODE], []) -LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive], - [m4_define([_LTDL_MODE], [nonrecursive])]) -LT_OPTION_DEFINE([LTDL_INIT], [recursive], - [m4_define([_LTDL_MODE], [recursive])]) -LT_OPTION_DEFINE([LTDL_INIT], [subproject], - [m4_define([_LTDL_MODE], [subproject])]) - -m4_define([_LTDL_TYPE], []) -LT_OPTION_DEFINE([LTDL_INIT], [installable], - [m4_define([_LTDL_TYPE], [installable])]) -LT_OPTION_DEFINE([LTDL_INIT], [convenience], - [m4_define([_LTDL_TYPE], [convenience])]) diff --git a/m4/ltsugar.m4 b/m4/ltsugar.m4 deleted file mode 100644 index 9000a05..0000000 --- a/m4/ltsugar.m4 +++ /dev/null @@ -1,123 +0,0 @@ -# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*- -# -# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc. -# Written by Gary V. Vaughan, 2004 -# -# This file 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. - -# serial 6 ltsugar.m4 - -# This is to help aclocal find these macros, as it can't see m4_define. -AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])]) - - -# lt_join(SEP, ARG1, [ARG2...]) -# ----------------------------- -# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their -# associated separator. -# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier -# versions in m4sugar had bugs. -m4_define([lt_join], -[m4_if([$#], [1], [], - [$#], [2], [[$2]], - [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])]) -m4_define([_lt_join], -[m4_if([$#$2], [2], [], - [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])]) - - -# lt_car(LIST) -# lt_cdr(LIST) -# ------------ -# Manipulate m4 lists. -# These macros are necessary as long as will still need to support -# Autoconf-2.59 which quotes differently. -m4_define([lt_car], [[$1]]) -m4_define([lt_cdr], -[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])], - [$#], 1, [], - [m4_dquote(m4_shift($@))])]) -m4_define([lt_unquote], $1) - - -# lt_append(MACRO-NAME, STRING, [SEPARATOR]) -# ------------------------------------------ -# Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'. -# Note that neither SEPARATOR nor STRING are expanded; they are appended -# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked). -# No SEPARATOR is output if MACRO-NAME was previously undefined (different -# than defined and empty). -# -# This macro is needed until we can rely on Autoconf 2.62, since earlier -# versions of m4sugar mistakenly expanded SEPARATOR but not STRING. -m4_define([lt_append], -[m4_define([$1], - m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])]) - - - -# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...]) -# ---------------------------------------------------------- -# Produce a SEP delimited list of all paired combinations of elements of -# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list -# has the form PREFIXmINFIXSUFFIXn. -# Needed until we can rely on m4_combine added in Autoconf 2.62. -m4_define([lt_combine], -[m4_if(m4_eval([$# > 3]), [1], - [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl -[[m4_foreach([_Lt_prefix], [$2], - [m4_foreach([_Lt_suffix], - ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[, - [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])]) - - -# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ]) -# ----------------------------------------------------------------------- -# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited -# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ. -m4_define([lt_if_append_uniq], -[m4_ifdef([$1], - [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1], - [lt_append([$1], [$2], [$3])$4], - [$5])], - [lt_append([$1], [$2], [$3])$4])]) - - -# lt_dict_add(DICT, KEY, VALUE) -# ----------------------------- -m4_define([lt_dict_add], -[m4_define([$1($2)], [$3])]) - - -# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE) -# -------------------------------------------- -m4_define([lt_dict_add_subkey], -[m4_define([$1($2:$3)], [$4])]) - - -# lt_dict_fetch(DICT, KEY, [SUBKEY]) -# ---------------------------------- -m4_define([lt_dict_fetch], -[m4_ifval([$3], - m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]), - m4_ifdef([$1($2)], [m4_defn([$1($2)])]))]) - - -# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE]) -# ----------------------------------------------------------------- -m4_define([lt_if_dict_fetch], -[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4], - [$5], - [$6])]) - - -# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...]) -# -------------------------------------------------------------- -m4_define([lt_dict_filter], -[m4_if([$5], [], [], - [lt_join(m4_quote(m4_default([$4], [[, ]])), - lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]), - [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl -]) diff --git a/m4/ltversion.m4 b/m4/ltversion.m4 deleted file mode 100644 index f3c5309..0000000 --- a/m4/ltversion.m4 +++ /dev/null @@ -1,23 +0,0 @@ -# ltversion.m4 -- version numbers -*- Autoconf -*- -# -# Copyright (C) 2004 Free Software Foundation, Inc. -# Written by Scott James Remnant, 2004 -# -# This file 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. - -# Generated from ltversion.in. - -# serial 3017 ltversion.m4 -# This file is part of GNU Libtool - -m4_define([LT_PACKAGE_VERSION], [2.2.6b]) -m4_define([LT_PACKAGE_REVISION], [1.3017]) - -AC_DEFUN([LTVERSION_VERSION], -[macro_version='2.2.6b' -macro_revision='1.3017' -_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) -_LT_DECL(, macro_revision, 0) -]) diff --git a/m4/lt~obsolete.m4 b/m4/lt~obsolete.m4 deleted file mode 100644 index 637bb20..0000000 --- a/m4/lt~obsolete.m4 +++ /dev/null @@ -1,92 +0,0 @@ -# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*- -# -# Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc. -# Written by Scott James Remnant, 2004. -# -# This file 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. - -# serial 4 lt~obsolete.m4 - -# These exist entirely to fool aclocal when bootstrapping libtool. -# -# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN) -# which have later been changed to m4_define as they aren't part of the -# exported API, or moved to Autoconf or Automake where they belong. -# -# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN -# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us -# using a macro with the same name in our local m4/libtool.m4 it'll -# pull the old libtool.m4 in (it doesn't see our shiny new m4_define -# and doesn't know about Autoconf macros at all.) -# -# So we provide this file, which has a silly filename so it's always -# included after everything else. This provides aclocal with the -# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything -# because those macros already exist, or will be overwritten later. -# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. -# -# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here. -# Yes, that means every name once taken will need to remain here until -# we give up compatibility with versions before 1.7, at which point -# we need to keep only those names which we still refer to. - -# This is to help aclocal find these macros, as it can't see m4_define. -AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])]) - -m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])]) -m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])]) -m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])]) -m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])]) -m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])]) -m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])]) -m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])]) -m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])]) -m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])]) -m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])]) -m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])]) -m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])]) -m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])]) -m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])]) -m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])]) -m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])]) -m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])]) -m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])]) -m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])]) -m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])]) -m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])]) -m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])]) -m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])]) -m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])]) -m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])]) -m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])]) -m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])]) -m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])]) -m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])]) -m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])]) -m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])]) -m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])]) -m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])]) -m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])]) -m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])]) -m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])]) -m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])]) -m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])]) -m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])]) -m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])]) -m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])]) -m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])]) -m4_ifndef([AC_LIBTOOL_RC], [AC_DEFUN([AC_LIBTOOL_RC])]) -m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])]) -m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])]) -m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])]) -m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])]) -m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])]) -m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])]) -m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])]) -m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])]) -m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])]) -m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])]) -m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])]) -m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])]) diff --git a/src/common.h b/src/common.h index ff3d5dd..5bf8686 100644 --- a/src/common.h +++ b/src/common.h @@ -1,4 +1,4 @@ -/* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*- +/* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*- * vim: ts=4 sw=4 noet ai cindent syntax=c */ @@ -64,6 +64,7 @@ double get_acpi_temperature(int fd); void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size, const char *adapter); void get_acpi_fan(char *, size_t); void get_battery_stuff(char *buf, unsigned int n, const char *bat, int item); +void get_dbus_stuff(char *buffer,unsigned int intMax_length, int item); int get_battery_perct(const char *bat); int get_battery_perct_bar(const char *bat); void get_battery_short_status(char *buf, unsigned int n, const char *bat); diff --git a/src/conky.c b/src/conky.c index bade353..86be8ed 100644 --- a/src/conky.c +++ b/src/conky.c @@ -894,6 +894,12 @@ void generate_text_internal(char *p, int p_max_size, OBJ(battery_short) { get_battery_short_status(p, p_max_size, obj->data.s); } + OBJ(cell_radio_dbm) { + get_dbus_stuff(p, p_max_size, DBUS_CELL_DBM); + } + OBJ(cell_radio_percent) { + get_dbus_stuff(p, p_max_size, DBUS_CELL_PERCENT); + } #endif /* __OpenBSD__ */ OBJ(buffers) { @@ -6029,7 +6035,7 @@ static void signal_handler(int sig) { /* signal handler is light as a feather, as it should be. * we will poll g_signal_pending with each loop of conky - * and do any signal processing there, NOT here (except + * and do any signal processing there, NOT here (except * SIGALRM because this is caused when conky is hanging) */ if(sig == SIGALRM) { alarm_handler(); diff --git a/src/conky.h b/src/conky.h index b717e38..442211d 100644 --- a/src/conky.h +++ b/src/conky.h @@ -197,13 +197,15 @@ extern struct conftree *currentconffile; #define MAX_TEMPLATES 10 char **get_templates(void); -/* get_battery_stuff() item selector +/* get_xxxxx_stuff() item selector * needed by conky.c, linux.c and freebsd.c */ enum { BATTERY_STATUS, BATTERY_TIME, BATTERY_VOLTS, - BATTERY_TEMP + BATTERY_TEMP, + DBUS_CELL_DBM, + DBUS_CELL_PERCENT }; /* if_up strictness selector diff --git a/src/core.c b/src/core.c index 009cb27..2ca922d 100644 --- a/src/core.c +++ b/src/core.c @@ -80,6 +80,7 @@ #include #include + /* strip a leading /dev/ if any, following symlinks first * * BEWARE: this function returns a pointer to static content @@ -160,12 +161,12 @@ struct text_object *construct_text_object(const char *s, const char *arg, long if(arg) { #ifdef __linux__ if(strpbrk(arg, "/.") != NULL) { - /* + /* * a bit of paranoia. screen out funky paths * i hope no device will have a '.' in its name */ NORM_ERR("acpiacadapter: arg must not contain '/' or '.'"); - } else + } else obj->data.opaque = strdup(arg); #else NORM_ERR("acpiacadapter: arg is only used on linux"); @@ -301,6 +302,8 @@ struct text_object *construct_text_object(const char *s, const char *arg, long strcpy(bat, "bq27200-0"); } obj->data.s = strndup(bat, text_buffer_size); + END OBJ(cell_radio_dbm, 0) + END OBJ(cell_radio_percent, 0) #endif /* !__OpenBSD__ */ #if defined(__linux__) diff --git a/src/dbus/.deps/dbus-address.Plo b/src/dbus/.deps/dbus-address.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-address.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-auth-script.Plo b/src/dbus/.deps/dbus-auth-script.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-auth-script.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-auth-util.Plo b/src/dbus/.deps/dbus-auth-util.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-auth-util.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-auth.Plo b/src/dbus/.deps/dbus-auth.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-auth.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-bus.Plo b/src/dbus/.deps/dbus-bus.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-bus.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-connection.Plo b/src/dbus/.deps/dbus-connection.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-connection.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-credentials-util.Plo b/src/dbus/.deps/dbus-credentials-util.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-credentials-util.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-credentials.Plo b/src/dbus/.deps/dbus-credentials.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-credentials.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-dataslot.Plo b/src/dbus/.deps/dbus-dataslot.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-dataslot.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-errors.Plo b/src/dbus/.deps/dbus-errors.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-errors.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-hash.Plo b/src/dbus/.deps/dbus-hash.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-hash.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-internals.Plo b/src/dbus/.deps/dbus-internals.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-internals.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-keyring.Plo b/src/dbus/.deps/dbus-keyring.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-keyring.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-list.Plo b/src/dbus/.deps/dbus-list.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-list.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-mainloop.Plo b/src/dbus/.deps/dbus-mainloop.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-mainloop.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-marshal-basic.Plo b/src/dbus/.deps/dbus-marshal-basic.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-marshal-basic.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-marshal-byteswap-util.Plo b/src/dbus/.deps/dbus-marshal-byteswap-util.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-marshal-byteswap-util.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-marshal-byteswap.Plo b/src/dbus/.deps/dbus-marshal-byteswap.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-marshal-byteswap.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-marshal-header.Plo b/src/dbus/.deps/dbus-marshal-header.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-marshal-header.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-marshal-recursive-util.Plo b/src/dbus/.deps/dbus-marshal-recursive-util.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-marshal-recursive-util.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-marshal-recursive.Plo b/src/dbus/.deps/dbus-marshal-recursive.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-marshal-recursive.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-marshal-validate-util.Plo b/src/dbus/.deps/dbus-marshal-validate-util.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-marshal-validate-util.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-marshal-validate.Plo b/src/dbus/.deps/dbus-marshal-validate.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-marshal-validate.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-memory.Plo b/src/dbus/.deps/dbus-memory.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-memory.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-mempool.Plo b/src/dbus/.deps/dbus-mempool.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-mempool.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-message-factory.Plo b/src/dbus/.deps/dbus-message-factory.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-message-factory.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-message-util.Plo b/src/dbus/.deps/dbus-message-util.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-message-util.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-message.Plo b/src/dbus/.deps/dbus-message.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-message.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-misc.Plo b/src/dbus/.deps/dbus-misc.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-misc.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-object-tree.Plo b/src/dbus/.deps/dbus-object-tree.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-object-tree.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-pending-call.Plo b/src/dbus/.deps/dbus-pending-call.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-pending-call.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-resources.Plo b/src/dbus/.deps/dbus-resources.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-resources.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-server-debug-pipe.Plo b/src/dbus/.deps/dbus-server-debug-pipe.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-server-debug-pipe.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-server-socket.Plo b/src/dbus/.deps/dbus-server-socket.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-server-socket.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-server-unix.Plo b/src/dbus/.deps/dbus-server-unix.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-server-unix.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-server.Plo b/src/dbus/.deps/dbus-server.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-server.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-sha.Plo b/src/dbus/.deps/dbus-sha.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-sha.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-shell.Plo b/src/dbus/.deps/dbus-shell.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-shell.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-signature.Plo b/src/dbus/.deps/dbus-signature.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-signature.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-spawn.Plo b/src/dbus/.deps/dbus-spawn.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-spawn.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-string-util.Plo b/src/dbus/.deps/dbus-string-util.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-string-util.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-string.Plo b/src/dbus/.deps/dbus-string.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-string.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-sysdeps-pthread.Plo b/src/dbus/.deps/dbus-sysdeps-pthread.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-sysdeps-pthread.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-sysdeps-unix.Plo b/src/dbus/.deps/dbus-sysdeps-unix.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-sysdeps-unix.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-sysdeps-util-unix.Plo b/src/dbus/.deps/dbus-sysdeps-util-unix.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-sysdeps-util-unix.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-sysdeps-util.Plo b/src/dbus/.deps/dbus-sysdeps-util.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-sysdeps-util.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-sysdeps.Plo b/src/dbus/.deps/dbus-sysdeps.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-sysdeps.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-test.Plo b/src/dbus/.deps/dbus-test.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-test.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-threads.Plo b/src/dbus/.deps/dbus-threads.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-threads.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-timeout.Plo b/src/dbus/.deps/dbus-timeout.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-timeout.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-transport-socket.Plo b/src/dbus/.deps/dbus-transport-socket.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-transport-socket.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-transport-unix.Plo b/src/dbus/.deps/dbus-transport-unix.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-transport-unix.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-transport.Plo b/src/dbus/.deps/dbus-transport.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-transport.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-userdb-util.Plo b/src/dbus/.deps/dbus-userdb-util.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-userdb-util.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-userdb.Plo b/src/dbus/.deps/dbus-userdb.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-userdb.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-uuidgen.Plo b/src/dbus/.deps/dbus-uuidgen.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-uuidgen.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/.deps/dbus-watch.Plo b/src/dbus/.deps/dbus-watch.Plo new file mode 100644 index 0000000..9ce06a8 --- /dev/null +++ b/src/dbus/.deps/dbus-watch.Plo @@ -0,0 +1 @@ +# dummy diff --git a/src/dbus/Makefile b/src/dbus/Makefile new file mode 100644 index 0000000..6a6266c --- /dev/null +++ b/src/dbus/Makefile @@ -0,0 +1,1026 @@ +# Makefile.in generated by automake 1.8.5 from Makefile.am. +# src/dbus/Makefile. Generated from Makefile.in by configure. + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004 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. + + + + + +SOURCES = $(libdbus_1_la_SOURCES) $(libdbus_convenience_la_SOURCES) $(dbus_test_SOURCES) + +srcdir = . +top_srcdir = ../.. + +pkgdatadir = $(datadir)/conky +pkglibdir = $(libdir)/conky +pkgincludedir = $(includedir)/conky +top_builddir = ../.. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = /scratchbox/tools/bin/install -c +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 = : +host_triplet = arm-unknown-linux-gnueabi +noinst_PROGRAMS = $(am__EXEEXT_1) +subdir = src/dbus +DIST_COMMON = $(dbusarchinclude_HEADERS) $(dbusinclude_HEADERS) \ + $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/pkg.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(mkdir_p) +CONFIG_HEADER = $(top_builddir)/src/config.h +CONFIG_CLEAN_FILES = +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(dbusarchincludedir)" "$(DESTDIR)$(dbusincludedir)" +libLTLIBRARIES_INSTALL = $(INSTALL) +LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) +libdbus_1_la_DEPENDENCIES = +am__objects_1 = dbus-address.lo dbus-auth.lo dbus-auth-script.lo \ + dbus-bus.lo dbus-connection.lo dbus-credentials.lo \ + dbus-errors.lo dbus-keyring.lo dbus-marshal-header.lo \ + dbus-marshal-byteswap.lo dbus-marshal-recursive.lo \ + dbus-marshal-validate.lo dbus-message.lo dbus-misc.lo \ + dbus-object-tree.lo dbus-pending-call.lo dbus-resources.lo \ + dbus-server.lo dbus-server-debug-pipe.lo dbus-server-socket.lo \ + dbus-server-unix.lo dbus-sha.lo dbus-signature.lo \ + dbus-timeout.lo dbus-threads.lo dbus-transport.lo \ + dbus-transport-socket.lo dbus-transport-unix.lo \ + dbus-uuidgen.lo dbus-watch.lo +am__objects_2 = dbus-dataslot.lo dbus-hash.lo dbus-internals.lo \ + dbus-list.lo dbus-marshal-basic.lo dbus-memory.lo \ + dbus-mempool.lo dbus-string.lo dbus-sysdeps.lo \ + dbus-sysdeps-pthread.lo dbus-sysdeps-unix.lo dbus-userdb.lo +am_libdbus_1_la_OBJECTS = $(am__objects_1) $(am__objects_2) +libdbus_1_la_OBJECTS = $(am_libdbus_1_la_OBJECTS) +libdbus_convenience_la_LIBADD = +am__objects_3 = dbus-auth-util.lo dbus-credentials-util.lo \ + dbus-mainloop.lo dbus-marshal-byteswap-util.lo \ + dbus-marshal-recursive-util.lo dbus-marshal-validate-util.lo \ + dbus-message-factory.lo dbus-message-util.lo dbus-shell.lo \ + dbus-spawn.lo dbus-string-util.lo dbus-sysdeps-util.lo \ + dbus-sysdeps-util-unix.lo dbus-test.lo dbus-userdb-util.lo +am_libdbus_convenience_la_OBJECTS = $(am__objects_1) $(am__objects_2) \ + $(am__objects_3) +libdbus_convenience_la_OBJECTS = $(am_libdbus_convenience_la_OBJECTS) +@DBUS_BUILD_TESTS_TRUE@am__EXEEXT_1 = dbus-test$(EXEEXT) +PROGRAMS = $(noinst_PROGRAMS) +am_dbus_test_OBJECTS = dbus-test-main.$(OBJEXT) +dbus_test_OBJECTS = $(am_dbus_test_OBJECTS) +dbus_test_DEPENDENCIES = libdbus-convenience.la +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/src +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +DEP_FILES = ./$(DEPDIR)/dbus-address.Plo \ + ./$(DEPDIR)/dbus-auth-script.Plo \ + ./$(DEPDIR)/dbus-auth-util.Plo \ + ./$(DEPDIR)/dbus-auth.Plo ./$(DEPDIR)/dbus-bus.Plo \ + ./$(DEPDIR)/dbus-connection.Plo \ + ./$(DEPDIR)/dbus-credentials-util.Plo \ + ./$(DEPDIR)/dbus-credentials.Plo \ + ./$(DEPDIR)/dbus-dataslot.Plo \ + ./$(DEPDIR)/dbus-errors.Plo \ + ./$(DEPDIR)/dbus-hash.Plo \ + ./$(DEPDIR)/dbus-internals.Plo \ + ./$(DEPDIR)/dbus-keyring.Plo \ + ./$(DEPDIR)/dbus-list.Plo \ + ./$(DEPDIR)/dbus-mainloop.Plo \ + ./$(DEPDIR)/dbus-marshal-basic.Plo \ + ./$(DEPDIR)/dbus-marshal-byteswap-util.Plo \ + ./$(DEPDIR)/dbus-marshal-byteswap.Plo \ + ./$(DEPDIR)/dbus-marshal-header.Plo \ + ./$(DEPDIR)/dbus-marshal-recursive-util.Plo \ + ./$(DEPDIR)/dbus-marshal-recursive.Plo \ + ./$(DEPDIR)/dbus-marshal-validate-util.Plo \ + ./$(DEPDIR)/dbus-marshal-validate.Plo \ + ./$(DEPDIR)/dbus-memory.Plo \ + ./$(DEPDIR)/dbus-mempool.Plo \ + ./$(DEPDIR)/dbus-message-factory.Plo \ + ./$(DEPDIR)/dbus-message-util.Plo \ + ./$(DEPDIR)/dbus-message.Plo \ + ./$(DEPDIR)/dbus-misc.Plo \ + ./$(DEPDIR)/dbus-object-tree.Plo \ + ./$(DEPDIR)/dbus-pending-call.Plo \ + ./$(DEPDIR)/dbus-resources.Plo \ + ./$(DEPDIR)/dbus-server-debug-pipe.Plo \ + ./$(DEPDIR)/dbus-server-socket.Plo \ + ./$(DEPDIR)/dbus-server-unix.Plo \ + ./$(DEPDIR)/dbus-server.Plo \ + ./$(DEPDIR)/dbus-sha.Plo \ + ./$(DEPDIR)/dbus-shell.Plo \ + ./$(DEPDIR)/dbus-signature.Plo \ + ./$(DEPDIR)/dbus-spawn.Plo \ + ./$(DEPDIR)/dbus-string-util.Plo \ + ./$(DEPDIR)/dbus-string.Plo \ + ./$(DEPDIR)/dbus-sysdeps-pthread.Plo \ + ./$(DEPDIR)/dbus-sysdeps-unix.Plo \ + ./$(DEPDIR)/dbus-sysdeps-util-unix.Plo \ + ./$(DEPDIR)/dbus-sysdeps-util.Plo \ + ./$(DEPDIR)/dbus-sysdeps.Plo \ + ./$(DEPDIR)/dbus-test-main.Po \ + ./$(DEPDIR)/dbus-test.Plo \ + ./$(DEPDIR)/dbus-threads.Plo \ + ./$(DEPDIR)/dbus-timeout.Plo \ + ./$(DEPDIR)/dbus-transport-socket.Plo \ + ./$(DEPDIR)/dbus-transport-unix.Plo \ + ./$(DEPDIR)/dbus-transport.Plo \ + ./$(DEPDIR)/dbus-userdb-util.Plo \ + ./$(DEPDIR)/dbus-userdb.Plo \ + ./$(DEPDIR)/dbus-uuidgen.Plo \ + ./$(DEPDIR)/dbus-watch.Plo +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(libdbus_1_la_SOURCES) $(libdbus_convenience_la_SOURCES) \ + $(dbus_test_SOURCES) +DIST_SOURCES = $(libdbus_1_la_SOURCES) \ + $(libdbus_convenience_la_SOURCES) $(dbus_test_SOURCES) +dbusarchincludeHEADERS_INSTALL = $(INSTALL_HEADER) +dbusincludeHEADERS_INSTALL = $(INSTALL_HEADER) +HEADERS = $(dbusarchinclude_HEADERS) $(dbusinclude_HEADERS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = ${SHELL} /home/lance/shared/workspace/conky/missing --run aclocal-1.8 +AMDEP_FALSE = # +AMDEP_TRUE = +AMTAR = ${SHELL} /home/lance/shared/workspace/conky/missing --run tar +AR = ar +AUTOCONF = ${SHELL} /home/lance/shared/workspace/conky/missing --run autoconf +AUTOHEADER = ${SHELL} /home/lance/shared/workspace/conky/missing --run autoheader +AUTOMAKE = ${SHELL} /home/lance/shared/workspace/conky/missing --run automake-1.8 +AWK = gawk +Audacious_CFLAGS = +Audacious_LIBS = +BMPx_CFLAGS = +BMPx_LIBS = +BUILD_APCUPSD_FALSE = # +BUILD_APCUPSD_TRUE = +BUILD_ARCH = Linux 2.6.32-24-generic (arm) +BUILD_AUDACIOUS_FALSE = +BUILD_AUDACIOUS_TRUE = # +BUILD_BMPX_FALSE = +BUILD_BMPX_TRUE = # +BUILD_CONFIG_OUTPUT_FALSE = # +BUILD_CONFIG_OUTPUT_TRUE = +BUILD_CURL_FALSE = +BUILD_CURL_TRUE = # +BUILD_DATE = Tue Oct 19 21:04:16 PDT 2010 +BUILD_EVE_FALSE = +BUILD_EVE_TRUE = # +BUILD_FREEBSD_FALSE = +BUILD_FREEBSD_TRUE = # +BUILD_HDDTEMP_FALSE = # +BUILD_HDDTEMP_TRUE = +BUILD_IBM_FALSE = +BUILD_IBM_TRUE = # +BUILD_ICONV_FALSE = # +BUILD_ICONV_TRUE = +BUILD_IMLIB2_FALSE = +BUILD_IMLIB2_TRUE = # +BUILD_LINUX_FALSE = # +BUILD_LINUX_TRUE = +BUILD_LUA_CAIRO_FALSE = +BUILD_LUA_CAIRO_TRUE = # +BUILD_LUA_FALSE = # +BUILD_LUA_IMLIB2_FALSE = +BUILD_LUA_IMLIB2_TRUE = # +BUILD_LUA_TRUE = +BUILD_MATH_FALSE = # +BUILD_MATH_TRUE = +BUILD_MOC_FALSE = # +BUILD_MOC_TRUE = +BUILD_MPD_FALSE = # +BUILD_MPD_TRUE = +BUILD_NCURSES_FALSE = # +BUILD_NCURSES_TRUE = +BUILD_NVIDIA_FALSE = +BUILD_NVIDIA_TRUE = # +BUILD_OPENBSD_FALSE = +BUILD_OPENBSD_TRUE = # +BUILD_PORT_MONITORS_FALSE = # +BUILD_PORT_MONITORS_TRUE = +BUILD_RSS_FALSE = +BUILD_RSS_TRUE = # +BUILD_WEATHER_FALSE = +BUILD_WEATHER_TRUE = # +BUILD_WLAN_FALSE = +BUILD_WLAN_TRUE = # +BUILD_X11_FALSE = # +BUILD_X11_TRUE = +BUILD_XMMS2_FALSE = +BUILD_XMMS2_TRUE = # +BUILD_XOAP_FALSE = +BUILD_XOAP_TRUE = # +CC = gcc +CCDEPMODE = depmode=gcc3 +CFLAGS = -O2 -g +CPP = gcc -E +CPPFLAGS = +CXX = g++ +CXXCPP = g++ -E +CXXDEPMODE = depmode=gcc3 +CXXFLAGS = -O2 -g +CYGPATH_W = echo +DEFS = -DHAVE_CONFIG_H +DEPDIR = .deps +DSYMUTIL = +ECHO = echo +ECHO_C = +ECHO_N = -n +ECHO_T = +EGREP = /scratchbox/tools/bin/grep -E +EXEEXT = +F77 = +FFLAGS = +GLib2_CFLAGS = -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include +GLib2_LIBS = -lglib-2.0 +GREP = /scratchbox/tools/bin/grep +HAVE_DOCSTUFF_FALSE = +HAVE_DOCSTUFF_TRUE = # +HAVE_PKGCONFIG = yes +INSTALL_DATA = ${INSTALL} -m 644 +INSTALL_PROGRAM = ${INSTALL} +INSTALL_SCRIPT = ${INSTALL} +INSTALL_STRIP_PROGRAM = ${SHELL} $(install_sh) -c -s +Imlib2_CFLAGS = +Imlib2_LIBS = +LDFLAGS = +LIBICONV = +LIBOBJS = +LIBS = -lrt +LIBTOOL = $(SHELL) $(top_builddir)/libtool +LN_S = ln +LTLIBICONV = +LTLIBOBJS = +LUA51_CFLAGS = -I/usr/include/lua5.1 +LUA51_LIBS = -llua5.1 +LUA_CFLAGS = +LUA_LIBS = +MAKEINFO = ${SHELL} /home/lance/shared/workspace/conky/missing --run makeinfo +NMEDIT = +OBJEXT = o +PACKAGE = conky +PACKAGE_BUGREPORT = brenden1@users.sourceforge.net +PACKAGE_NAME = Conky +PACKAGE_STRING = Conky 1.8.0 +PACKAGE_TARNAME = conky +PACKAGE_VERSION = 1.8.0 +PATH_SEPARATOR = : +PKG_CONFIG = /scratchbox/tools/bin/pkg-config +RANLIB = ranlib +SED = /scratchbox/tools/bin/sed +SET_MAKE = +SHELL = /bin/sh +STRIP = strip +VERSION = 1.8.0 +X11_CFLAGS = +X11_LIBS = -lX11 +XDamage_CFLAGS = +XDamage_LIBS = -lXdamage -lXfixes +XMKMF = +XMMS2_CFLAGS = +XMMS2_LIBS = +X_CFLAGS = +X_EXTRA_LIBS = +X_LIBS = +X_PRE_LIBS = +Xext_CFLAGS = +Xext_LIBS = -lXext +Xft_CFLAGS = -I/usr/include/freetype2 +Xft_LIBS = -lXft -lfontconfig +ac_ct_CC = gcc +ac_ct_CXX = g++ +ac_ct_F77 = +am__fastdepCC_FALSE = # +am__fastdepCC_TRUE = +am__fastdepCXX_FALSE = # +am__fastdepCXX_TRUE = +am__include = include +am__leading_dot = . +am__quote = +bindir = ${exec_prefix}/bin +build = arm-unknown-linux-gnueabi +build_alias = +build_cpu = arm +build_os = linux-gnueabi +build_vendor = unknown +cairo_CFLAGS = +cairo_LIBS = +cairo_xlib_CFLAGS = +cairo_xlib_LIBS = +conky_CFLAGS = -I/usr/include/lua5.1 -I/usr/include/freetype2 -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -Wall -W +conky_LIBS = -lncurses -lm -lX11 -llua5.1 -lXext -lXdamage -lXfixes -lXft -lfontconfig -lglib-2.0 -lasound -lrt +datadir = ${datarootdir} +datarootdir = ${prefix}/share +db2x_manxml_cmd = +db2x_xsltproc_cmd = +docdir = ${datarootdir}/doc/${PACKAGE_TARNAME} +dvidir = ${docdir} +exec_prefix = ${prefix} +host = arm-unknown-linux-gnueabi +host_alias = +host_cpu = arm +host_os = linux-gnueabi +host_vendor = unknown +htmldir = ${docdir} +includedir = ${prefix}/include +infodir = ${datarootdir}/info +install_sh = /home/lance/shared/workspace/conky/install-sh +libcurl_CFLAGS = +libcurl_LIBS = +libdir = ${exec_prefix}/lib +libexecdir = ${exec_prefix}/libexec +libxml2_CFLAGS = +libxml2_LIBS = +localedir = ${datarootdir}/locale +localstatedir = ${prefix}/var +mandir = ${datarootdir}/man +mkdir_p = mkdir -p -- . +oldincludedir = /usr/include +pdfdir = ${docdir} +prefix = /usr/local +program_transform_name = s,x,x, +psdir = ${docdir} +sbindir = ${exec_prefix}/sbin +sharedstatedir = ${prefix}/com +sysconfdir = ${prefix}/etc +target_alias = +tolua_CFLAGS = +tolua_LIBS = +toluapp = +xsltproc_cmd = xsltproc +configdir = $(sysconfdir)/dbus-1 +INCLUDES = -I$(top_builddir) -I$(top_srcdir) $(DBUS_CLIENT_CFLAGS) @PIC_CFLAGS@ -DDBUS_COMPILATION \ + -DDBUS_MACHINE_UUID_FILE=\""$(localstatedir)/lib/dbus/machine-id"\" \ + -DDBUS_SYSTEM_CONFIG_FILE=\""$(configdir)/system.conf"\" \ + -DDBUS_SESSION_CONFIG_FILE=\""$(configdir)/session.conf"\" + +dbusincludedir = $(includedir)/dbus-1.0/dbus +dbusarchincludedir = $(libdir)/dbus-1.0/include/dbus +lib_LTLIBRARIES = libdbus-1.la +dbusinclude_HEADERS = \ + dbus.h \ + dbus-address.h \ + dbus-bus.h \ + dbus-connection.h \ + dbus-errors.h \ + dbus-macros.h \ + dbus-memory.h \ + dbus-message.h \ + dbus-misc.h \ + dbus-pending-call.h \ + dbus-protocol.h \ + dbus-server.h \ + dbus-shared.h \ + dbus-signature.h \ + dbus-threads.h \ + dbus-types.h + +dbusarchinclude_HEADERS = \ + dbus-arch-deps.h + + +### source code that goes in the installed client library +### and is specific to library functionality +DBUS_LIB_SOURCES = \ + dbus-address.c \ + dbus-auth.c \ + dbus-auth.h \ + dbus-auth-script.c \ + dbus-auth-script.h \ + dbus-bus.c \ + dbus-connection.c \ + dbus-connection-internal.h \ + dbus-credentials.c \ + dbus-credentials.h \ + dbus-errors.c \ + dbus-keyring.c \ + dbus-keyring.h \ + dbus-marshal-header.c \ + dbus-marshal-header.h \ + dbus-marshal-byteswap.c \ + dbus-marshal-byteswap.h \ + dbus-marshal-recursive.c \ + dbus-marshal-recursive.h \ + dbus-marshal-validate.c \ + dbus-marshal-validate.h \ + dbus-message.c \ + dbus-message-internal.h \ + dbus-message-private.h \ + dbus-misc.c \ + dbus-object-tree.c \ + dbus-object-tree.h \ + dbus-pending-call.c \ + dbus-pending-call-internal.h \ + dbus-resources.c \ + dbus-resources.h \ + dbus-server.c \ + dbus-server-debug-pipe.c \ + dbus-server-debug-pipe.h \ + dbus-server-protected.h \ + dbus-server-socket.c \ + dbus-server-socket.h \ + dbus-server-unix.c \ + dbus-server-unix.h \ + dbus-sha.c \ + dbus-sha.h \ + dbus-signature.c \ + dbus-timeout.c \ + dbus-timeout.h \ + dbus-threads-internal.h \ + dbus-threads.c \ + dbus-transport.c \ + dbus-transport.h \ + dbus-transport-protected.h \ + dbus-transport-socket.c \ + dbus-transport-socket.h \ + dbus-transport-unix.c \ + dbus-transport-unix.h \ + dbus-uuidgen.c \ + dbus-uuidgen.h \ + dbus-watch.c \ + dbus-watch.h + + +### source code that goes in the installed client library +### AND is generic utility functionality used by the +### daemon or test programs (all symbols in here should +### be underscore-prefixed) +DBUS_SHARED_SOURCES = \ + dbus-dataslot.c \ + dbus-dataslot.h \ + dbus-hash.c \ + dbus-hash.h \ + dbus-internals.c \ + dbus-internals.h \ + dbus-list.c \ + dbus-list.h \ + dbus-marshal-basic.c \ + dbus-marshal-basic.h \ + dbus-memory.c \ + dbus-mempool.c \ + dbus-mempool.h \ + dbus-string.c \ + dbus-string.h \ + dbus-string-private.h \ + dbus-sysdeps.c \ + dbus-sysdeps.h \ + dbus-sysdeps-pthread.c \ + dbus-sysdeps-unix.c \ + dbus-sysdeps-unix.h \ + dbus-userdb.c \ + dbus-userdb.h + + +### source code that is generic utility functionality used +### by the bus daemon or test apps, but is NOT included +### in the D-Bus client library (all symbols in here +### should be underscore-prefixed but don't really need +### to be unless they move to DBUS_SHARED_SOURCES later) +DBUS_UTIL_SOURCES = \ + dbus-auth-util.c \ + dbus-credentials-util.c \ + dbus-mainloop.c \ + dbus-mainloop.h \ + dbus-marshal-byteswap-util.c \ + dbus-marshal-recursive-util.c \ + dbus-marshal-validate-util.c \ + dbus-message-factory.c \ + dbus-message-factory.h \ + dbus-message-util.c \ + dbus-shell.c \ + dbus-shell.h \ + dbus-spawn.c \ + dbus-spawn.h \ + dbus-string-util.c \ + dbus-sysdeps-util.c \ + dbus-sysdeps-util-unix.c \ + dbus-test.c \ + dbus-test.h \ + dbus-userdb-util.c + +libdbus_1_la_SOURCES = \ + $(DBUS_LIB_SOURCES) \ + $(DBUS_SHARED_SOURCES) + +libdbus_convenience_la_SOURCES = \ + $(DBUS_LIB_SOURCES) \ + $(DBUS_SHARED_SOURCES) \ + $(DBUS_UTIL_SOURCES) + +BUILT_SOURCES = $(dbusarchinclude_HEADERS) +EXTRA_DIST = dbus-arch-deps.h.in +noinst_LTLIBRARIES = libdbus-convenience.la +libdbus_1_la_LIBADD = $(DBUS_CLIENT_LIBS) +libdbus_1_la_LDFLAGS = -export-symbols-regex "^[^_].*" -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) -no-undefined @R_DYNAMIC_LDFLAG@ @PIC_LDFLAGS@ +libdbus_convenience_la_LDFLAGS = @R_DYNAMIC_LDFLAG@ +@DBUS_BUILD_TESTS_TRUE@TESTS_ENVIRONMENT = DBUS_TEST_DATA=$(top_builddir)/test/data DBUS_TEST_HOMEDIR=$(top_builddir)/dbus +@DBUS_BUILD_TESTS_FALSE@TESTS = +@DBUS_BUILD_TESTS_TRUE@TESTS = dbus-test +dbus_test_SOURCES = \ + dbus-test-main.c + +dbus_test_LDADD = libdbus-convenience.la $(DBUS_TEST_LIBS) +dbus_test_LDFLAGS = @R_DYNAMIC_LDFLAG@ +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/dbus/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/dbus/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: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(libdir)" || $(mkdir_p) "$(DESTDIR)$(libdir)" + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + if test -f $$p; then \ + f="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(libdir)/$$f"; \ + else :; fi; \ + done + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + p="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " $(LIBTOOL) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \ + $(LIBTOOL) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libdbus-1.la: $(libdbus_1_la_OBJECTS) $(libdbus_1_la_DEPENDENCIES) + $(LINK) -rpath $(libdir) $(libdbus_1_la_LDFLAGS) $(libdbus_1_la_OBJECTS) $(libdbus_1_la_LIBADD) $(LIBS) +libdbus-convenience.la: $(libdbus_convenience_la_OBJECTS) $(libdbus_convenience_la_DEPENDENCIES) + $(LINK) $(libdbus_convenience_la_LDFLAGS) $(libdbus_convenience_la_OBJECTS) $(libdbus_convenience_la_LIBADD) $(LIBS) + +clean-noinstPROGRAMS: + @list='$(noinst_PROGRAMS)'; for p in $$list; do \ + f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f $$p $$f"; \ + rm -f $$p $$f ; \ + done +dbus-test$(EXEEXT): $(dbus_test_OBJECTS) $(dbus_test_DEPENDENCIES) + @rm -f dbus-test$(EXEEXT) + $(LINK) $(dbus_test_LDFLAGS) $(dbus_test_OBJECTS) $(dbus_test_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +include ./$(DEPDIR)/dbus-address.Plo +include ./$(DEPDIR)/dbus-auth-script.Plo +include ./$(DEPDIR)/dbus-auth-util.Plo +include ./$(DEPDIR)/dbus-auth.Plo +include ./$(DEPDIR)/dbus-bus.Plo +include ./$(DEPDIR)/dbus-connection.Plo +include ./$(DEPDIR)/dbus-credentials-util.Plo +include ./$(DEPDIR)/dbus-credentials.Plo +include ./$(DEPDIR)/dbus-dataslot.Plo +include ./$(DEPDIR)/dbus-errors.Plo +include ./$(DEPDIR)/dbus-hash.Plo +include ./$(DEPDIR)/dbus-internals.Plo +include ./$(DEPDIR)/dbus-keyring.Plo +include ./$(DEPDIR)/dbus-list.Plo +include ./$(DEPDIR)/dbus-mainloop.Plo +include ./$(DEPDIR)/dbus-marshal-basic.Plo +include ./$(DEPDIR)/dbus-marshal-byteswap-util.Plo +include ./$(DEPDIR)/dbus-marshal-byteswap.Plo +include ./$(DEPDIR)/dbus-marshal-header.Plo +include ./$(DEPDIR)/dbus-marshal-recursive-util.Plo +include ./$(DEPDIR)/dbus-marshal-recursive.Plo +include ./$(DEPDIR)/dbus-marshal-validate-util.Plo +include ./$(DEPDIR)/dbus-marshal-validate.Plo +include ./$(DEPDIR)/dbus-memory.Plo +include ./$(DEPDIR)/dbus-mempool.Plo +include ./$(DEPDIR)/dbus-message-factory.Plo +include ./$(DEPDIR)/dbus-message-util.Plo +include ./$(DEPDIR)/dbus-message.Plo +include ./$(DEPDIR)/dbus-misc.Plo +include ./$(DEPDIR)/dbus-object-tree.Plo +include ./$(DEPDIR)/dbus-pending-call.Plo +include ./$(DEPDIR)/dbus-resources.Plo +include ./$(DEPDIR)/dbus-server-debug-pipe.Plo +include ./$(DEPDIR)/dbus-server-socket.Plo +include ./$(DEPDIR)/dbus-server-unix.Plo +include ./$(DEPDIR)/dbus-server.Plo +include ./$(DEPDIR)/dbus-sha.Plo +include ./$(DEPDIR)/dbus-shell.Plo +include ./$(DEPDIR)/dbus-signature.Plo +include ./$(DEPDIR)/dbus-spawn.Plo +include ./$(DEPDIR)/dbus-string-util.Plo +include ./$(DEPDIR)/dbus-string.Plo +include ./$(DEPDIR)/dbus-sysdeps-pthread.Plo +include ./$(DEPDIR)/dbus-sysdeps-unix.Plo +include ./$(DEPDIR)/dbus-sysdeps-util-unix.Plo +include ./$(DEPDIR)/dbus-sysdeps-util.Plo +include ./$(DEPDIR)/dbus-sysdeps.Plo +include ./$(DEPDIR)/dbus-test-main.Po +include ./$(DEPDIR)/dbus-test.Plo +include ./$(DEPDIR)/dbus-threads.Plo +include ./$(DEPDIR)/dbus-timeout.Plo +include ./$(DEPDIR)/dbus-transport-socket.Plo +include ./$(DEPDIR)/dbus-transport-unix.Plo +include ./$(DEPDIR)/dbus-transport.Plo +include ./$(DEPDIR)/dbus-userdb-util.Plo +include ./$(DEPDIR)/dbus-userdb.Plo +include ./$(DEPDIR)/dbus-uuidgen.Plo +include ./$(DEPDIR)/dbus-watch.Plo + +.c.o: + if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ + then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +# source='$<' object='$@' libtool=no \ +# depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' \ +# $(CCDEPMODE) $(depcomp) \ +# $(COMPILE) -c $< + +.c.obj: + if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ + then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +# source='$<' object='$@' libtool=no \ +# depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' \ +# $(CCDEPMODE) $(depcomp) \ +# $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: + if $(LTCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ + then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +# source='$<' object='$@' libtool=yes \ +# depfile='$(DEPDIR)/$*.Plo' tmpdepfile='$(DEPDIR)/$*.TPlo' \ +# $(CCDEPMODE) $(depcomp) \ +# $(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: +install-dbusarchincludeHEADERS: $(dbusarchinclude_HEADERS) + @$(NORMAL_INSTALL) + test -z "$(dbusarchincludedir)" || $(mkdir_p) "$(DESTDIR)$(dbusarchincludedir)" + @list='$(dbusarchinclude_HEADERS)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + f="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " $(dbusarchincludeHEADERS_INSTALL) '$$d$$p' '$(DESTDIR)$(dbusarchincludedir)/$$f'"; \ + $(dbusarchincludeHEADERS_INSTALL) "$$d$$p" "$(DESTDIR)$(dbusarchincludedir)/$$f"; \ + done + +uninstall-dbusarchincludeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(dbusarchinclude_HEADERS)'; for p in $$list; do \ + f="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " rm -f '$(DESTDIR)$(dbusarchincludedir)/$$f'"; \ + rm -f "$(DESTDIR)$(dbusarchincludedir)/$$f"; \ + done +install-dbusincludeHEADERS: $(dbusinclude_HEADERS) + @$(NORMAL_INSTALL) + test -z "$(dbusincludedir)" || $(mkdir_p) "$(DESTDIR)$(dbusincludedir)" + @list='$(dbusinclude_HEADERS)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + f="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " $(dbusincludeHEADERS_INSTALL) '$$d$$p' '$(DESTDIR)$(dbusincludedir)/$$f'"; \ + $(dbusincludeHEADERS_INSTALL) "$$d$$p" "$(DESTDIR)$(dbusincludedir)/$$f"; \ + done + +uninstall-dbusincludeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(dbusinclude_HEADERS)'; for p in $$list; do \ + f="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " rm -f '$(DESTDIR)$(dbusincludedir)/$$f'"; \ + rm -f "$(DESTDIR)$(dbusincludedir)/$$f"; \ + done + +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; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + 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; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + 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; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && 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)'; \ + 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`; \ + echo "XPASS: $$tst"; \ + ;; \ + *) \ + echo "PASS: $$tst"; \ + ;; \ + esac; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *" $$tst "*) \ + xfail=`expr $$xfail + 1`; \ + echo "XFAIL: $$tst"; \ + ;; \ + *) \ + failed=`expr $$failed + 1`; \ + echo "FAIL: $$tst"; \ + ;; \ + esac; \ + else \ + skip=`expr $$skip + 1`; \ + echo "SKIP: $$tst"; \ + fi; \ + done; \ + if test "$$failed" -eq 0; then \ + if test "$$xfail" -eq 0; then \ + banner="All $$all tests passed"; \ + else \ + 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 \ + banner="$$failed of $$all tests did not behave as expected ($$xpass unexpected passes)"; \ + fi; \ + fi; \ + dashes="$$banner"; \ + skipped=""; \ + if test "$$skip" -ne 0; then \ + skipped="($$skip tests were not run)"; \ + 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`; \ + echo "$$dashes"; \ + echo "$$banner"; \ + test -z "$$skipped" || echo "$$skipped"; \ + test -z "$$report" || echo "$$report"; \ + echo "$$dashes"; \ + test "$$failed" -eq 0; \ + else :; fi + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$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: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(dbusarchincludedir)" "$(DESTDIR)$(dbusincludedir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) 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: + -rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) +clean: clean-am + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool clean-local \ + clean-noinstLTLIBRARIES clean-noinstPROGRAMS mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-libtool distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: install-dbusarchincludeHEADERS \ + install-dbusincludeHEADERS + +install-exec-am: install-libLTLIBRARIES + +install-info: install-info-am + +install-man: + +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-dbusarchincludeHEADERS \ + uninstall-dbusincludeHEADERS uninstall-info-am \ + uninstall-libLTLIBRARIES + +.PHONY: CTAGS GTAGS all all-am check check-TESTS check-am clean \ + clean-generic clean-libLTLIBRARIES clean-libtool clean-local \ + clean-noinstLTLIBRARIES 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-data install-data-am \ + install-dbusarchincludeHEADERS install-dbusincludeHEADERS \ + install-exec install-exec-am install-info install-info-am \ + install-libLTLIBRARIES install-man 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-dbusarchincludeHEADERS \ + uninstall-dbusincludeHEADERS uninstall-info-am \ + uninstall-libLTLIBRARIES + + +clean-local: + /bin/rm *.bb *.bbg *.da *.gcov .libs/*.da .libs/*.bbg || true +# 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/src/dbus/Makefile.am b/src/dbus/Makefile.am new file mode 100644 index 0000000..e966a43 --- /dev/null +++ b/src/dbus/Makefile.am @@ -0,0 +1,202 @@ + +configdir=$(sysconfdir)/dbus-1 + +INCLUDES=-I$(top_builddir) -I$(top_srcdir) $(DBUS_CLIENT_CFLAGS) @PIC_CFLAGS@ -DDBUS_COMPILATION \ + -DDBUS_MACHINE_UUID_FILE=\""$(localstatedir)/lib/dbus/machine-id"\" \ + -DDBUS_SYSTEM_CONFIG_FILE=\""$(configdir)/system.conf"\" \ + -DDBUS_SESSION_CONFIG_FILE=\""$(configdir)/session.conf"\" + +dbusincludedir=$(includedir)/dbus-1.0/dbus +dbusarchincludedir=$(libdir)/dbus-1.0/include/dbus + +lib_LTLIBRARIES=libdbus-1.la + +dbusinclude_HEADERS= \ + dbus.h \ + dbus-address.h \ + dbus-bus.h \ + dbus-connection.h \ + dbus-errors.h \ + dbus-macros.h \ + dbus-memory.h \ + dbus-message.h \ + dbus-misc.h \ + dbus-pending-call.h \ + dbus-protocol.h \ + dbus-server.h \ + dbus-shared.h \ + dbus-signature.h \ + dbus-threads.h \ + dbus-types.h + + +dbusarchinclude_HEADERS= \ + dbus-arch-deps.h + +### source code that goes in the installed client library +### and is specific to library functionality +DBUS_LIB_SOURCES= \ + dbus-address.c \ + dbus-auth.c \ + dbus-auth.h \ + dbus-auth-script.c \ + dbus-auth-script.h \ + dbus-bus.c \ + dbus-connection.c \ + dbus-connection-internal.h \ + dbus-credentials.c \ + dbus-credentials.h \ + dbus-errors.c \ + dbus-keyring.c \ + dbus-keyring.h \ + dbus-marshal-header.c \ + dbus-marshal-header.h \ + dbus-marshal-byteswap.c \ + dbus-marshal-byteswap.h \ + dbus-marshal-recursive.c \ + dbus-marshal-recursive.h \ + dbus-marshal-validate.c \ + dbus-marshal-validate.h \ + dbus-message.c \ + dbus-message-internal.h \ + dbus-message-private.h \ + dbus-misc.c \ + dbus-object-tree.c \ + dbus-object-tree.h \ + dbus-pending-call.c \ + dbus-pending-call-internal.h \ + dbus-resources.c \ + dbus-resources.h \ + dbus-server.c \ + dbus-server-debug-pipe.c \ + dbus-server-debug-pipe.h \ + dbus-server-protected.h \ + dbus-server-socket.c \ + dbus-server-socket.h \ + dbus-server-unix.c \ + dbus-server-unix.h \ + dbus-sha.c \ + dbus-sha.h \ + dbus-signature.c \ + dbus-timeout.c \ + dbus-timeout.h \ + dbus-threads-internal.h \ + dbus-threads.c \ + dbus-transport.c \ + dbus-transport.h \ + dbus-transport-protected.h \ + dbus-transport-socket.c \ + dbus-transport-socket.h \ + dbus-transport-unix.c \ + dbus-transport-unix.h \ + dbus-uuidgen.c \ + dbus-uuidgen.h \ + dbus-watch.c \ + dbus-watch.h + +## dbus-md5.c \ +## dbus-md5.h \ + +### source code that goes in the installed client library +### AND is generic utility functionality used by the +### daemon or test programs (all symbols in here should +### be underscore-prefixed) +DBUS_SHARED_SOURCES= \ + dbus-dataslot.c \ + dbus-dataslot.h \ + dbus-hash.c \ + dbus-hash.h \ + dbus-internals.c \ + dbus-internals.h \ + dbus-list.c \ + dbus-list.h \ + dbus-marshal-basic.c \ + dbus-marshal-basic.h \ + dbus-memory.c \ + dbus-mempool.c \ + dbus-mempool.h \ + dbus-string.c \ + dbus-string.h \ + dbus-string-private.h \ + dbus-sysdeps.c \ + dbus-sysdeps.h \ + dbus-sysdeps-pthread.c \ + dbus-sysdeps-unix.c \ + dbus-sysdeps-unix.h \ + dbus-userdb.c \ + dbus-userdb.h + +### source code that is generic utility functionality used +### by the bus daemon or test apps, but is NOT included +### in the D-Bus client library (all symbols in here +### should be underscore-prefixed but don't really need +### to be unless they move to DBUS_SHARED_SOURCES later) +DBUS_UTIL_SOURCES= \ + dbus-auth-util.c \ + dbus-credentials-util.c \ + dbus-mainloop.c \ + dbus-mainloop.h \ + dbus-marshal-byteswap-util.c \ + dbus-marshal-recursive-util.c \ + dbus-marshal-validate-util.c \ + dbus-message-factory.c \ + dbus-message-factory.h \ + dbus-message-util.c \ + dbus-shell.c \ + dbus-shell.h \ + dbus-spawn.c \ + dbus-spawn.h \ + dbus-string-util.c \ + dbus-sysdeps-util.c \ + dbus-sysdeps-util-unix.c \ + dbus-test.c \ + dbus-test.h \ + dbus-userdb-util.c + +libdbus_1_la_SOURCES= \ + $(DBUS_LIB_SOURCES) \ + $(DBUS_SHARED_SOURCES) + +libdbus_convenience_la_SOURCES= \ + $(DBUS_LIB_SOURCES) \ + $(DBUS_SHARED_SOURCES) \ + $(DBUS_UTIL_SOURCES) + + +BUILT_SOURCES=$(dbusarchinclude_HEADERS) +EXTRA_DIST=dbus-arch-deps.h.in + +## this library is the same as libdbus, but exports all the symbols +## and is only used for static linking within the dbus package. +noinst_LTLIBRARIES=libdbus-convenience.la + +libdbus_1_la_LIBADD= $(DBUS_CLIENT_LIBS) +## don't export symbols that start with "_" (we use this +## convention for internal symbols) +libdbus_1_la_LDFLAGS= -export-symbols-regex "^[^_].*" -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) -no-undefined @R_DYNAMIC_LDFLAG@ @PIC_LDFLAGS@ + +libdbus_convenience_la_LDFLAGS=@R_DYNAMIC_LDFLAG@ + +## 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 +TESTS=dbus-test +else +TESTS= +endif + +## we use noinst_PROGRAMS not check_PROGRAMS so that we build +## even when not doing "make check" +noinst_PROGRAMS=$(TESTS) + +dbus_test_SOURCES= \ + dbus-test-main.c + +dbus_test_LDADD=libdbus-convenience.la $(DBUS_TEST_LIBS) +dbus_test_LDFLAGS=@R_DYNAMIC_LDFLAG@ + +## mop up the gcov files +clean-local: + /bin/rm *.bb *.bbg *.da *.gcov .libs/*.da .libs/*.bbg || true diff --git a/src/dbus/Makefile.in b/src/dbus/Makefile.in new file mode 100644 index 0000000..caf9380 --- /dev/null +++ b/src/dbus/Makefile.in @@ -0,0 +1,1026 @@ +# Makefile.in generated by automake 1.8.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004 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@ + + + +SOURCES = $(libdbus_1_la_SOURCES) $(libdbus_convenience_la_SOURCES) $(dbus_test_SOURCES) + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +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 = : +host_triplet = @host@ +noinst_PROGRAMS = $(am__EXEEXT_1) +subdir = src/dbus +DIST_COMMON = $(dbusarchinclude_HEADERS) $(dbusinclude_HEADERS) \ + $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/pkg.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(mkdir_p) +CONFIG_HEADER = $(top_builddir)/src/config.h +CONFIG_CLEAN_FILES = +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(dbusarchincludedir)" "$(DESTDIR)$(dbusincludedir)" +libLTLIBRARIES_INSTALL = $(INSTALL) +LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) +libdbus_1_la_DEPENDENCIES = +am__objects_1 = dbus-address.lo dbus-auth.lo dbus-auth-script.lo \ + dbus-bus.lo dbus-connection.lo dbus-credentials.lo \ + dbus-errors.lo dbus-keyring.lo dbus-marshal-header.lo \ + dbus-marshal-byteswap.lo dbus-marshal-recursive.lo \ + dbus-marshal-validate.lo dbus-message.lo dbus-misc.lo \ + dbus-object-tree.lo dbus-pending-call.lo dbus-resources.lo \ + dbus-server.lo dbus-server-debug-pipe.lo dbus-server-socket.lo \ + dbus-server-unix.lo dbus-sha.lo dbus-signature.lo \ + dbus-timeout.lo dbus-threads.lo dbus-transport.lo \ + dbus-transport-socket.lo dbus-transport-unix.lo \ + dbus-uuidgen.lo dbus-watch.lo +am__objects_2 = dbus-dataslot.lo dbus-hash.lo dbus-internals.lo \ + dbus-list.lo dbus-marshal-basic.lo dbus-memory.lo \ + dbus-mempool.lo dbus-string.lo dbus-sysdeps.lo \ + dbus-sysdeps-pthread.lo dbus-sysdeps-unix.lo dbus-userdb.lo +am_libdbus_1_la_OBJECTS = $(am__objects_1) $(am__objects_2) +libdbus_1_la_OBJECTS = $(am_libdbus_1_la_OBJECTS) +libdbus_convenience_la_LIBADD = +am__objects_3 = dbus-auth-util.lo dbus-credentials-util.lo \ + dbus-mainloop.lo dbus-marshal-byteswap-util.lo \ + dbus-marshal-recursive-util.lo dbus-marshal-validate-util.lo \ + dbus-message-factory.lo dbus-message-util.lo dbus-shell.lo \ + dbus-spawn.lo dbus-string-util.lo dbus-sysdeps-util.lo \ + dbus-sysdeps-util-unix.lo dbus-test.lo dbus-userdb-util.lo +am_libdbus_convenience_la_OBJECTS = $(am__objects_1) $(am__objects_2) \ + $(am__objects_3) +libdbus_convenience_la_OBJECTS = $(am_libdbus_convenience_la_OBJECTS) +@DBUS_BUILD_TESTS_TRUE@am__EXEEXT_1 = dbus-test$(EXEEXT) +PROGRAMS = $(noinst_PROGRAMS) +am_dbus_test_OBJECTS = dbus-test-main.$(OBJEXT) +dbus_test_OBJECTS = $(am_dbus_test_OBJECTS) +dbus_test_DEPENDENCIES = libdbus-convenience.la +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/src +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/dbus-address.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-auth-script.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-auth-util.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-auth.Plo ./$(DEPDIR)/dbus-bus.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-connection.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-credentials-util.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-credentials.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-dataslot.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-errors.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-hash.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-internals.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-keyring.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-list.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-mainloop.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-marshal-basic.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-marshal-byteswap-util.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-marshal-byteswap.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-marshal-header.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-marshal-recursive-util.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-marshal-recursive.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-marshal-validate-util.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-marshal-validate.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-memory.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-mempool.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-message-factory.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-message-util.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-message.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-misc.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-object-tree.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-pending-call.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-resources.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-server-debug-pipe.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-server-socket.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-server-unix.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-server.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-sha.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-shell.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-signature.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-spawn.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-string-util.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-string.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-sysdeps-pthread.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-sysdeps-unix.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-sysdeps-util-unix.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-sysdeps-util.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-sysdeps.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-test-main.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-test.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-threads.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-timeout.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-transport-socket.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-transport-unix.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-transport.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-userdb-util.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-userdb.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-uuidgen.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dbus-watch.Plo +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(libdbus_1_la_SOURCES) $(libdbus_convenience_la_SOURCES) \ + $(dbus_test_SOURCES) +DIST_SOURCES = $(libdbus_1_la_SOURCES) \ + $(libdbus_convenience_la_SOURCES) $(dbus_test_SOURCES) +dbusarchincludeHEADERS_INSTALL = $(INSTALL_HEADER) +dbusincludeHEADERS_INSTALL = $(INSTALL_HEADER) +HEADERS = $(dbusarchinclude_HEADERS) $(dbusinclude_HEADERS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +Audacious_CFLAGS = @Audacious_CFLAGS@ +Audacious_LIBS = @Audacious_LIBS@ +BMPx_CFLAGS = @BMPx_CFLAGS@ +BMPx_LIBS = @BMPx_LIBS@ +BUILD_APCUPSD_FALSE = @BUILD_APCUPSD_FALSE@ +BUILD_APCUPSD_TRUE = @BUILD_APCUPSD_TRUE@ +BUILD_ARCH = @BUILD_ARCH@ +BUILD_AUDACIOUS_FALSE = @BUILD_AUDACIOUS_FALSE@ +BUILD_AUDACIOUS_TRUE = @BUILD_AUDACIOUS_TRUE@ +BUILD_BMPX_FALSE = @BUILD_BMPX_FALSE@ +BUILD_BMPX_TRUE = @BUILD_BMPX_TRUE@ +BUILD_CONFIG_OUTPUT_FALSE = @BUILD_CONFIG_OUTPUT_FALSE@ +BUILD_CONFIG_OUTPUT_TRUE = @BUILD_CONFIG_OUTPUT_TRUE@ +BUILD_CURL_FALSE = @BUILD_CURL_FALSE@ +BUILD_CURL_TRUE = @BUILD_CURL_TRUE@ +BUILD_DATE = @BUILD_DATE@ +BUILD_EVE_FALSE = @BUILD_EVE_FALSE@ +BUILD_EVE_TRUE = @BUILD_EVE_TRUE@ +BUILD_FREEBSD_FALSE = @BUILD_FREEBSD_FALSE@ +BUILD_FREEBSD_TRUE = @BUILD_FREEBSD_TRUE@ +BUILD_HDDTEMP_FALSE = @BUILD_HDDTEMP_FALSE@ +BUILD_HDDTEMP_TRUE = @BUILD_HDDTEMP_TRUE@ +BUILD_IBM_FALSE = @BUILD_IBM_FALSE@ +BUILD_IBM_TRUE = @BUILD_IBM_TRUE@ +BUILD_ICONV_FALSE = @BUILD_ICONV_FALSE@ +BUILD_ICONV_TRUE = @BUILD_ICONV_TRUE@ +BUILD_IMLIB2_FALSE = @BUILD_IMLIB2_FALSE@ +BUILD_IMLIB2_TRUE = @BUILD_IMLIB2_TRUE@ +BUILD_LINUX_FALSE = @BUILD_LINUX_FALSE@ +BUILD_LINUX_TRUE = @BUILD_LINUX_TRUE@ +BUILD_LUA_CAIRO_FALSE = @BUILD_LUA_CAIRO_FALSE@ +BUILD_LUA_CAIRO_TRUE = @BUILD_LUA_CAIRO_TRUE@ +BUILD_LUA_FALSE = @BUILD_LUA_FALSE@ +BUILD_LUA_IMLIB2_FALSE = @BUILD_LUA_IMLIB2_FALSE@ +BUILD_LUA_IMLIB2_TRUE = @BUILD_LUA_IMLIB2_TRUE@ +BUILD_LUA_TRUE = @BUILD_LUA_TRUE@ +BUILD_MATH_FALSE = @BUILD_MATH_FALSE@ +BUILD_MATH_TRUE = @BUILD_MATH_TRUE@ +BUILD_MOC_FALSE = @BUILD_MOC_FALSE@ +BUILD_MOC_TRUE = @BUILD_MOC_TRUE@ +BUILD_MPD_FALSE = @BUILD_MPD_FALSE@ +BUILD_MPD_TRUE = @BUILD_MPD_TRUE@ +BUILD_NCURSES_FALSE = @BUILD_NCURSES_FALSE@ +BUILD_NCURSES_TRUE = @BUILD_NCURSES_TRUE@ +BUILD_NVIDIA_FALSE = @BUILD_NVIDIA_FALSE@ +BUILD_NVIDIA_TRUE = @BUILD_NVIDIA_TRUE@ +BUILD_OPENBSD_FALSE = @BUILD_OPENBSD_FALSE@ +BUILD_OPENBSD_TRUE = @BUILD_OPENBSD_TRUE@ +BUILD_PORT_MONITORS_FALSE = @BUILD_PORT_MONITORS_FALSE@ +BUILD_PORT_MONITORS_TRUE = @BUILD_PORT_MONITORS_TRUE@ +BUILD_RSS_FALSE = @BUILD_RSS_FALSE@ +BUILD_RSS_TRUE = @BUILD_RSS_TRUE@ +BUILD_WEATHER_FALSE = @BUILD_WEATHER_FALSE@ +BUILD_WEATHER_TRUE = @BUILD_WEATHER_TRUE@ +BUILD_WLAN_FALSE = @BUILD_WLAN_FALSE@ +BUILD_WLAN_TRUE = @BUILD_WLAN_TRUE@ +BUILD_X11_FALSE = @BUILD_X11_FALSE@ +BUILD_X11_TRUE = @BUILD_X11_TRUE@ +BUILD_XMMS2_FALSE = @BUILD_XMMS2_FALSE@ +BUILD_XMMS2_TRUE = @BUILD_XMMS2_TRUE@ +BUILD_XOAP_FALSE = @BUILD_XOAP_FALSE@ +BUILD_XOAP_TRUE = @BUILD_XOAP_TRUE@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DSYMUTIL = @DSYMUTIL@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +GLib2_CFLAGS = @GLib2_CFLAGS@ +GLib2_LIBS = @GLib2_LIBS@ +GREP = @GREP@ +HAVE_DOCSTUFF_FALSE = @HAVE_DOCSTUFF_FALSE@ +HAVE_DOCSTUFF_TRUE = @HAVE_DOCSTUFF_TRUE@ +HAVE_PKGCONFIG = @HAVE_PKGCONFIG@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +Imlib2_CFLAGS = @Imlib2_CFLAGS@ +Imlib2_LIBS = @Imlib2_LIBS@ +LDFLAGS = @LDFLAGS@ +LIBICONV = @LIBICONV@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBOBJS = @LTLIBOBJS@ +LUA51_CFLAGS = @LUA51_CFLAGS@ +LUA51_LIBS = @LUA51_LIBS@ +LUA_CFLAGS = @LUA_CFLAGS@ +LUA_LIBS = @LUA_LIBS@ +MAKEINFO = @MAKEINFO@ +NMEDIT = @NMEDIT@ +OBJEXT = @OBJEXT@ +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@ +PKG_CONFIG = @PKG_CONFIG@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +X11_CFLAGS = @X11_CFLAGS@ +X11_LIBS = @X11_LIBS@ +XDamage_CFLAGS = @XDamage_CFLAGS@ +XDamage_LIBS = @XDamage_LIBS@ +XMKMF = @XMKMF@ +XMMS2_CFLAGS = @XMMS2_CFLAGS@ +XMMS2_LIBS = @XMMS2_LIBS@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +Xext_CFLAGS = @Xext_CFLAGS@ +Xext_LIBS = @Xext_LIBS@ +Xft_CFLAGS = @Xft_CFLAGS@ +Xft_LIBS = @Xft_LIBS@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +cairo_CFLAGS = @cairo_CFLAGS@ +cairo_LIBS = @cairo_LIBS@ +cairo_xlib_CFLAGS = @cairo_xlib_CFLAGS@ +cairo_xlib_LIBS = @cairo_xlib_LIBS@ +conky_CFLAGS = @conky_CFLAGS@ +conky_LIBS = @conky_LIBS@ +datadir = @datadir@ +datarootdir = @datarootdir@ +db2x_manxml_cmd = @db2x_manxml_cmd@ +db2x_xsltproc_cmd = @db2x_xsltproc_cmd@ +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@ +libcurl_CFLAGS = @libcurl_CFLAGS@ +libcurl_LIBS = @libcurl_LIBS@ +libdir = @libdir@ +libexecdir = @libexecdir@ +libxml2_CFLAGS = @libxml2_CFLAGS@ +libxml2_LIBS = @libxml2_LIBS@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +tolua_CFLAGS = @tolua_CFLAGS@ +tolua_LIBS = @tolua_LIBS@ +toluapp = @toluapp@ +xsltproc_cmd = @xsltproc_cmd@ +configdir = $(sysconfdir)/dbus-1 +INCLUDES = -I$(top_builddir) -I$(top_srcdir) $(DBUS_CLIENT_CFLAGS) @PIC_CFLAGS@ -DDBUS_COMPILATION \ + -DDBUS_MACHINE_UUID_FILE=\""$(localstatedir)/lib/dbus/machine-id"\" \ + -DDBUS_SYSTEM_CONFIG_FILE=\""$(configdir)/system.conf"\" \ + -DDBUS_SESSION_CONFIG_FILE=\""$(configdir)/session.conf"\" + +dbusincludedir = $(includedir)/dbus-1.0/dbus +dbusarchincludedir = $(libdir)/dbus-1.0/include/dbus +lib_LTLIBRARIES = libdbus-1.la +dbusinclude_HEADERS = \ + dbus.h \ + dbus-address.h \ + dbus-bus.h \ + dbus-connection.h \ + dbus-errors.h \ + dbus-macros.h \ + dbus-memory.h \ + dbus-message.h \ + dbus-misc.h \ + dbus-pending-call.h \ + dbus-protocol.h \ + dbus-server.h \ + dbus-shared.h \ + dbus-signature.h \ + dbus-threads.h \ + dbus-types.h + +dbusarchinclude_HEADERS = \ + dbus-arch-deps.h + + +### source code that goes in the installed client library +### and is specific to library functionality +DBUS_LIB_SOURCES = \ + dbus-address.c \ + dbus-auth.c \ + dbus-auth.h \ + dbus-auth-script.c \ + dbus-auth-script.h \ + dbus-bus.c \ + dbus-connection.c \ + dbus-connection-internal.h \ + dbus-credentials.c \ + dbus-credentials.h \ + dbus-errors.c \ + dbus-keyring.c \ + dbus-keyring.h \ + dbus-marshal-header.c \ + dbus-marshal-header.h \ + dbus-marshal-byteswap.c \ + dbus-marshal-byteswap.h \ + dbus-marshal-recursive.c \ + dbus-marshal-recursive.h \ + dbus-marshal-validate.c \ + dbus-marshal-validate.h \ + dbus-message.c \ + dbus-message-internal.h \ + dbus-message-private.h \ + dbus-misc.c \ + dbus-object-tree.c \ + dbus-object-tree.h \ + dbus-pending-call.c \ + dbus-pending-call-internal.h \ + dbus-resources.c \ + dbus-resources.h \ + dbus-server.c \ + dbus-server-debug-pipe.c \ + dbus-server-debug-pipe.h \ + dbus-server-protected.h \ + dbus-server-socket.c \ + dbus-server-socket.h \ + dbus-server-unix.c \ + dbus-server-unix.h \ + dbus-sha.c \ + dbus-sha.h \ + dbus-signature.c \ + dbus-timeout.c \ + dbus-timeout.h \ + dbus-threads-internal.h \ + dbus-threads.c \ + dbus-transport.c \ + dbus-transport.h \ + dbus-transport-protected.h \ + dbus-transport-socket.c \ + dbus-transport-socket.h \ + dbus-transport-unix.c \ + dbus-transport-unix.h \ + dbus-uuidgen.c \ + dbus-uuidgen.h \ + dbus-watch.c \ + dbus-watch.h + + +### source code that goes in the installed client library +### AND is generic utility functionality used by the +### daemon or test programs (all symbols in here should +### be underscore-prefixed) +DBUS_SHARED_SOURCES = \ + dbus-dataslot.c \ + dbus-dataslot.h \ + dbus-hash.c \ + dbus-hash.h \ + dbus-internals.c \ + dbus-internals.h \ + dbus-list.c \ + dbus-list.h \ + dbus-marshal-basic.c \ + dbus-marshal-basic.h \ + dbus-memory.c \ + dbus-mempool.c \ + dbus-mempool.h \ + dbus-string.c \ + dbus-string.h \ + dbus-string-private.h \ + dbus-sysdeps.c \ + dbus-sysdeps.h \ + dbus-sysdeps-pthread.c \ + dbus-sysdeps-unix.c \ + dbus-sysdeps-unix.h \ + dbus-userdb.c \ + dbus-userdb.h + + +### source code that is generic utility functionality used +### by the bus daemon or test apps, but is NOT included +### in the D-Bus client library (all symbols in here +### should be underscore-prefixed but don't really need +### to be unless they move to DBUS_SHARED_SOURCES later) +DBUS_UTIL_SOURCES = \ + dbus-auth-util.c \ + dbus-credentials-util.c \ + dbus-mainloop.c \ + dbus-mainloop.h \ + dbus-marshal-byteswap-util.c \ + dbus-marshal-recursive-util.c \ + dbus-marshal-validate-util.c \ + dbus-message-factory.c \ + dbus-message-factory.h \ + dbus-message-util.c \ + dbus-shell.c \ + dbus-shell.h \ + dbus-spawn.c \ + dbus-spawn.h \ + dbus-string-util.c \ + dbus-sysdeps-util.c \ + dbus-sysdeps-util-unix.c \ + dbus-test.c \ + dbus-test.h \ + dbus-userdb-util.c + +libdbus_1_la_SOURCES = \ + $(DBUS_LIB_SOURCES) \ + $(DBUS_SHARED_SOURCES) + +libdbus_convenience_la_SOURCES = \ + $(DBUS_LIB_SOURCES) \ + $(DBUS_SHARED_SOURCES) \ + $(DBUS_UTIL_SOURCES) + +BUILT_SOURCES = $(dbusarchinclude_HEADERS) +EXTRA_DIST = dbus-arch-deps.h.in +noinst_LTLIBRARIES = libdbus-convenience.la +libdbus_1_la_LIBADD = $(DBUS_CLIENT_LIBS) +libdbus_1_la_LDFLAGS = -export-symbols-regex "^[^_].*" -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) -no-undefined @R_DYNAMIC_LDFLAG@ @PIC_LDFLAGS@ +libdbus_convenience_la_LDFLAGS = @R_DYNAMIC_LDFLAG@ +@DBUS_BUILD_TESTS_TRUE@TESTS_ENVIRONMENT = DBUS_TEST_DATA=$(top_builddir)/test/data DBUS_TEST_HOMEDIR=$(top_builddir)/dbus +@DBUS_BUILD_TESTS_FALSE@TESTS = +@DBUS_BUILD_TESTS_TRUE@TESTS = dbus-test +dbus_test_SOURCES = \ + dbus-test-main.c + +dbus_test_LDADD = libdbus-convenience.la $(DBUS_TEST_LIBS) +dbus_test_LDFLAGS = @R_DYNAMIC_LDFLAG@ +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/dbus/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/dbus/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: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(libdir)" || $(mkdir_p) "$(DESTDIR)$(libdir)" + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + if test -f $$p; then \ + f="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(libdir)/$$f"; \ + else :; fi; \ + done + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + p="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " $(LIBTOOL) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \ + $(LIBTOOL) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libdbus-1.la: $(libdbus_1_la_OBJECTS) $(libdbus_1_la_DEPENDENCIES) + $(LINK) -rpath $(libdir) $(libdbus_1_la_LDFLAGS) $(libdbus_1_la_OBJECTS) $(libdbus_1_la_LIBADD) $(LIBS) +libdbus-convenience.la: $(libdbus_convenience_la_OBJECTS) $(libdbus_convenience_la_DEPENDENCIES) + $(LINK) $(libdbus_convenience_la_LDFLAGS) $(libdbus_convenience_la_OBJECTS) $(libdbus_convenience_la_LIBADD) $(LIBS) + +clean-noinstPROGRAMS: + @list='$(noinst_PROGRAMS)'; for p in $$list; do \ + f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f $$p $$f"; \ + rm -f $$p $$f ; \ + done +dbus-test$(EXEEXT): $(dbus_test_OBJECTS) $(dbus_test_DEPENDENCIES) + @rm -f dbus-test$(EXEEXT) + $(LINK) $(dbus_test_LDFLAGS) $(dbus_test_OBJECTS) $(dbus_test_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-address.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-auth-script.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-auth-util.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-auth.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-bus.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-credentials-util.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-credentials.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-dataslot.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-errors.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-hash.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-internals.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-keyring.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-list.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-mainloop.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-marshal-basic.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-marshal-byteswap-util.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-marshal-byteswap.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-marshal-header.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-marshal-recursive-util.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-marshal-recursive.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-marshal-validate-util.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-marshal-validate.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-memory.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-mempool.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-message-factory.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-message-util.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-message.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-misc.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-object-tree.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-pending-call.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-resources.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-server-debug-pipe.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-server-socket.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-server-unix.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-server.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-sha.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-shell.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-signature.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-spawn.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-string-util.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-string.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-sysdeps-pthread.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-sysdeps-unix.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-sysdeps-util-unix.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-sysdeps-util.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-sysdeps.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-test-main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-test.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-threads.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-timeout.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-transport-socket.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-transport-unix.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-transport.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-userdb-util.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-userdb.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-uuidgen.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-watch.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ if $(LTCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/$*.Plo' tmpdepfile='$(DEPDIR)/$*.TPlo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: +install-dbusarchincludeHEADERS: $(dbusarchinclude_HEADERS) + @$(NORMAL_INSTALL) + test -z "$(dbusarchincludedir)" || $(mkdir_p) "$(DESTDIR)$(dbusarchincludedir)" + @list='$(dbusarchinclude_HEADERS)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + f="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " $(dbusarchincludeHEADERS_INSTALL) '$$d$$p' '$(DESTDIR)$(dbusarchincludedir)/$$f'"; \ + $(dbusarchincludeHEADERS_INSTALL) "$$d$$p" "$(DESTDIR)$(dbusarchincludedir)/$$f"; \ + done + +uninstall-dbusarchincludeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(dbusarchinclude_HEADERS)'; for p in $$list; do \ + f="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " rm -f '$(DESTDIR)$(dbusarchincludedir)/$$f'"; \ + rm -f "$(DESTDIR)$(dbusarchincludedir)/$$f"; \ + done +install-dbusincludeHEADERS: $(dbusinclude_HEADERS) + @$(NORMAL_INSTALL) + test -z "$(dbusincludedir)" || $(mkdir_p) "$(DESTDIR)$(dbusincludedir)" + @list='$(dbusinclude_HEADERS)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + f="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " $(dbusincludeHEADERS_INSTALL) '$$d$$p' '$(DESTDIR)$(dbusincludedir)/$$f'"; \ + $(dbusincludeHEADERS_INSTALL) "$$d$$p" "$(DESTDIR)$(dbusincludedir)/$$f"; \ + done + +uninstall-dbusincludeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(dbusinclude_HEADERS)'; for p in $$list; do \ + f="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " rm -f '$(DESTDIR)$(dbusincludedir)/$$f'"; \ + rm -f "$(DESTDIR)$(dbusincludedir)/$$f"; \ + done + +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; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + 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; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + 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; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && 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)'; \ + 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`; \ + echo "XPASS: $$tst"; \ + ;; \ + *) \ + echo "PASS: $$tst"; \ + ;; \ + esac; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *" $$tst "*) \ + xfail=`expr $$xfail + 1`; \ + echo "XFAIL: $$tst"; \ + ;; \ + *) \ + failed=`expr $$failed + 1`; \ + echo "FAIL: $$tst"; \ + ;; \ + esac; \ + else \ + skip=`expr $$skip + 1`; \ + echo "SKIP: $$tst"; \ + fi; \ + done; \ + if test "$$failed" -eq 0; then \ + if test "$$xfail" -eq 0; then \ + banner="All $$all tests passed"; \ + else \ + 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 \ + banner="$$failed of $$all tests did not behave as expected ($$xpass unexpected passes)"; \ + fi; \ + fi; \ + dashes="$$banner"; \ + skipped=""; \ + if test "$$skip" -ne 0; then \ + skipped="($$skip tests were not run)"; \ + 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`; \ + echo "$$dashes"; \ + echo "$$banner"; \ + test -z "$$skipped" || echo "$$skipped"; \ + test -z "$$report" || echo "$$report"; \ + echo "$$dashes"; \ + test "$$failed" -eq 0; \ + else :; fi + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$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: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(dbusarchincludedir)" "$(DESTDIR)$(dbusincludedir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) 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: + -rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) +clean: clean-am + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool clean-local \ + clean-noinstLTLIBRARIES clean-noinstPROGRAMS mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-libtool distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: install-dbusarchincludeHEADERS \ + install-dbusincludeHEADERS + +install-exec-am: install-libLTLIBRARIES + +install-info: install-info-am + +install-man: + +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-dbusarchincludeHEADERS \ + uninstall-dbusincludeHEADERS uninstall-info-am \ + uninstall-libLTLIBRARIES + +.PHONY: CTAGS GTAGS all all-am check check-TESTS check-am clean \ + clean-generic clean-libLTLIBRARIES clean-libtool clean-local \ + clean-noinstLTLIBRARIES 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-data install-data-am \ + install-dbusarchincludeHEADERS install-dbusincludeHEADERS \ + install-exec install-exec-am install-info install-info-am \ + install-libLTLIBRARIES install-man 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-dbusarchincludeHEADERS \ + uninstall-dbusincludeHEADERS uninstall-info-am \ + uninstall-libLTLIBRARIES + + +clean-local: + /bin/rm *.bb *.bbg *.da *.gcov .libs/*.da .libs/*.bbg || true +# 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/src/dbus/dbus-address.c b/src/dbus/dbus-address.c new file mode 100644 index 0000000..28e8842 --- /dev/null +++ b/src/dbus/dbus-address.c @@ -0,0 +1,826 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-address.c Server address parser. + * + * Copyright (C) 2003 CodeFactory AB + * Copyright (C) 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include "dbus-address.h" +#include "dbus-internals.h" +#include "dbus-list.h" +#include "dbus-string.h" +#include "dbus-protocol.h" + +/** + * @defgroup DBusAddressInternals Address parsing + * @ingroup DBusInternals + * @brief Implementation of parsing addresses of D-Bus servers. + * + * @{ + */ + +/** + * Internals of DBusAddressEntry + */ +struct DBusAddressEntry +{ + DBusString method; /**< The address type (unix, tcp, etc.) */ + + DBusList *keys; /**< List of keys */ + DBusList *values; /**< List of values */ +}; + + +/** + * + * Sets #DBUS_ERROR_BAD_ADDRESS. + * If address_problem_type and address_problem_field are not #NULL, + * sets an error message about how the field is no good. Otherwise, sets + * address_problem_other as the error message. + * + * @param error the error to set + * @param address_problem_type the address type of the bad address or #NULL + * @param address_problem_field the missing field of the bad address or #NULL + * @param address_problem_other any other error message or #NULL + */ +void +_dbus_set_bad_address (DBusError *error, + const char *address_problem_type, + const char *address_problem_field, + const char *address_problem_other) +{ + if (address_problem_type != NULL) + dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, + "Server address of type %s was missing argument %s", + address_problem_type, address_problem_field); + else + dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, + "Could not parse server address: %s", + address_problem_other); +} + +/** + * #TRUE if the byte need not be escaped when found in a dbus address. + * All other bytes are required to be escaped in a valid address. + */ +#define _DBUS_ADDRESS_OPTIONALLY_ESCAPED_BYTE(b) \ + (((b) >= 'a' && (b) <= 'z') || \ + ((b) >= 'A' && (b) <= 'Z') || \ + ((b) >= '0' && (b) <= '9') || \ + (b) == '-' || \ + (b) == '_' || \ + (b) == '/' || \ + (b) == '\\' || \ + (b) == '*' || \ + (b) == '.') + +/** + * Appends an escaped version of one string to another string, + * using the D-Bus address escaping mechanism + * + * @param escaped the string to append to + * @param unescaped the string to escape + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_address_append_escaped (DBusString *escaped, + const DBusString *unescaped) +{ + const char *p; + const char *end; + dbus_bool_t ret; + int orig_len; + + ret = FALSE; + + orig_len = _dbus_string_get_length (escaped); + p = _dbus_string_get_const_data (unescaped); + end = p + _dbus_string_get_length (unescaped); + while (p != end) + { + if (_DBUS_ADDRESS_OPTIONALLY_ESCAPED_BYTE (*p)) + { + if (!_dbus_string_append_byte (escaped, *p)) + goto out; + } + else + { + if (!_dbus_string_append_byte (escaped, '%')) + goto out; + if (!_dbus_string_append_byte_as_hex (escaped, *p)) + goto out; + } + + ++p; + } + + ret = TRUE; + + out: + if (!ret) + _dbus_string_set_length (escaped, orig_len); + return ret; +} + +/** @} */ /* End of internals */ + +static void +dbus_address_entry_free (DBusAddressEntry *entry) +{ + DBusList *link; + + _dbus_string_free (&entry->method); + + link = _dbus_list_get_first_link (&entry->keys); + while (link != NULL) + { + _dbus_string_free (link->data); + dbus_free (link->data); + + link = _dbus_list_get_next_link (&entry->keys, link); + } + _dbus_list_clear (&entry->keys); + + link = _dbus_list_get_first_link (&entry->values); + while (link != NULL) + { + _dbus_string_free (link->data); + dbus_free (link->data); + + link = _dbus_list_get_next_link (&entry->values, link); + } + _dbus_list_clear (&entry->values); + + dbus_free (entry); +} + +/** + * @defgroup DBusAddress Address parsing + * @ingroup DBus + * @brief Parsing addresses of D-Bus servers. + * + * @{ + */ + +/** + * Frees a #NULL-terminated array of address entries. + * + * @param entries the array. + */ +void +dbus_address_entries_free (DBusAddressEntry **entries) +{ + int i; + + for (i = 0; entries[i] != NULL; i++) + dbus_address_entry_free (entries[i]); + dbus_free (entries); +} + +static DBusAddressEntry * +create_entry (void) +{ + DBusAddressEntry *entry; + + entry = dbus_new0 (DBusAddressEntry, 1); + + if (entry == NULL) + return NULL; + + if (!_dbus_string_init (&entry->method)) + { + dbus_free (entry); + return NULL; + } + + return entry; +} + +/** + * Returns the method string of an address entry. For example, given + * the address entry "tcp:host=example.com" it would return the string + * "tcp" + * + * @param entry the entry. + * @returns a string describing the method. This string + * must not be freed. + */ +const char * +dbus_address_entry_get_method (DBusAddressEntry *entry) +{ + return _dbus_string_get_const_data (&entry->method); +} + +/** + * Returns a value from a key of an entry. For example, + * given the address "tcp:host=example.com,port=8073" if you asked + * for the key "host" you would get the value "example.com" + * + * The returned value is already unescaped. + * + * @param entry the entry. + * @param key the key. + * @returns the key value. This string must not be freed. + */ +const char * +dbus_address_entry_get_value (DBusAddressEntry *entry, + const char *key) +{ + DBusList *values, *keys; + + keys = _dbus_list_get_first_link (&entry->keys); + values = _dbus_list_get_first_link (&entry->values); + + while (keys != NULL) + { + _dbus_assert (values != NULL); + + if (_dbus_string_equal_c_str (keys->data, key)) + return _dbus_string_get_const_data (values->data); + + keys = _dbus_list_get_next_link (&entry->keys, keys); + values = _dbus_list_get_next_link (&entry->values, values); + } + + return NULL; +} + +static dbus_bool_t +append_unescaped_value (DBusString *unescaped, + const DBusString *escaped, + int escaped_start, + int escaped_len, + DBusError *error) +{ + const char *p; + const char *end; + dbus_bool_t ret; + + ret = FALSE; + + p = _dbus_string_get_const_data (escaped) + escaped_start; + end = p + escaped_len; + while (p != end) + { + if (_DBUS_ADDRESS_OPTIONALLY_ESCAPED_BYTE (*p)) + { + if (!_dbus_string_append_byte (unescaped, *p)) + goto out; + } + else if (*p == '%') + { + /* Efficiency is king */ + char buf[3]; + DBusString hex; + int hex_end; + + ++p; + + if ((p + 2) > end) + { + dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, + "In D-Bus address, percent character was not followed by two hex digits"); + goto out; + } + + buf[0] = *p; + ++p; + buf[1] = *p; + buf[2] = '\0'; + + _dbus_string_init_const (&hex, buf); + + if (!_dbus_string_hex_decode (&hex, 0, &hex_end, + unescaped, + _dbus_string_get_length (unescaped))) + goto out; + + if (hex_end != 2) + { + dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, + "In D-Bus address, percent character was followed by characters other than hex digits"); + goto out; + } + } + else + { + /* Error, should have been escaped */ + dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, + "In D-Bus address, character '%c' should have been escaped\n", + *p); + goto out; + } + + ++p; + } + + ret = TRUE; + + out: + if (!ret && error && !dbus_error_is_set (error)) + _DBUS_SET_OOM (error); + + _dbus_assert (ret || error == NULL || dbus_error_is_set (error)); + + return ret; +} + +/** + * Parses an address string of the form: + * + * method:key=value,key=value;method:key=value + * + * See the D-Bus specification for complete docs on the format. + * + * When connecting to an address, the first address entries + * in the semicolon-separated list should be tried first. + * + * @param address the address. + * @param entry return location to an array of entries. + * @param array_len return location for array length. + * @param error address where an error can be returned. + * @returns #TRUE on success, #FALSE otherwise. + */ +dbus_bool_t +dbus_parse_address (const char *address, + DBusAddressEntry ***entry, + int *array_len, + DBusError *error) +{ + DBusString str; + int pos, end_pos, len, i; + DBusList *entries, *link; + DBusAddressEntry **entry_array; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + _dbus_string_init_const (&str, address); + + entries = NULL; + pos = 0; + len = _dbus_string_get_length (&str); + + if (len == 0) + { + dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, + "Empty address '%s'", address); + goto error; + } + + while (pos < len) + { + DBusAddressEntry *entry; + + int found_pos; + + entry = create_entry (); + if (!entry) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + + goto error; + } + + /* Append the entry */ + if (!_dbus_list_append (&entries, entry)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + dbus_address_entry_free (entry); + goto error; + } + + /* Look for a semi-colon */ + if (!_dbus_string_find (&str, pos, ";", &end_pos)) + end_pos = len; + + /* Look for the colon : */ + if (!_dbus_string_find_to (&str, pos, end_pos, ":", &found_pos)) + { + dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, "Address does not contain a colon"); + goto error; + } + + if (!_dbus_string_copy_len (&str, pos, found_pos - pos, &entry->method, 0)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto error; + } + + pos = found_pos + 1; + + while (pos < end_pos) + { + int comma_pos, equals_pos; + + if (!_dbus_string_find_to (&str, pos, end_pos, ",", &comma_pos)) + comma_pos = end_pos; + + if (!_dbus_string_find_to (&str, pos, comma_pos, "=", &equals_pos) || + equals_pos == pos || equals_pos + 1 == comma_pos) + { + dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, + "'=' character not found or has no value following it"); + goto error; + } + else + { + DBusString *key; + DBusString *value; + + key = dbus_new0 (DBusString, 1); + + if (!key) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto error; + } + + value = dbus_new0 (DBusString, 1); + if (!value) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + dbus_free (key); + goto error; + } + + if (!_dbus_string_init (key)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + dbus_free (key); + dbus_free (value); + + goto error; + } + + if (!_dbus_string_init (value)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + _dbus_string_free (key); + + dbus_free (key); + dbus_free (value); + goto error; + } + + if (!_dbus_string_copy_len (&str, pos, equals_pos - pos, key, 0)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + _dbus_string_free (key); + _dbus_string_free (value); + + dbus_free (key); + dbus_free (value); + goto error; + } + + if (!append_unescaped_value (value, &str, equals_pos + 1, + comma_pos - equals_pos - 1, error)) + { + _dbus_assert (error == NULL || dbus_error_is_set (error)); + _dbus_string_free (key); + _dbus_string_free (value); + + dbus_free (key); + dbus_free (value); + goto error; + } + + if (!_dbus_list_append (&entry->keys, key)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + _dbus_string_free (key); + _dbus_string_free (value); + + dbus_free (key); + dbus_free (value); + goto error; + } + + if (!_dbus_list_append (&entry->values, value)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + _dbus_string_free (value); + + dbus_free (value); + goto error; + } + } + + pos = comma_pos + 1; + } + + pos = end_pos + 1; + } + + *array_len = _dbus_list_get_length (&entries); + + entry_array = dbus_new (DBusAddressEntry *, *array_len + 1); + + if (!entry_array) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + + goto error; + } + + entry_array [*array_len] = NULL; + + link = _dbus_list_get_first_link (&entries); + i = 0; + while (link != NULL) + { + entry_array[i] = link->data; + i++; + link = _dbus_list_get_next_link (&entries, link); + } + + _dbus_list_clear (&entries); + *entry = entry_array; + + return TRUE; + + error: + + link = _dbus_list_get_first_link (&entries); + while (link != NULL) + { + dbus_address_entry_free (link->data); + link = _dbus_list_get_next_link (&entries, link); + } + + _dbus_list_clear (&entries); + + return FALSE; + +} + +/** + * Escapes the given string as a value in a key=value pair + * for a D-Bus address. + * + * @param value the unescaped value + * @returns newly-allocated escaped value or #NULL if no memory + */ +char* +dbus_address_escape_value (const char *value) +{ + DBusString escaped; + DBusString unescaped; + char *ret; + + ret = NULL; + + _dbus_string_init_const (&unescaped, value); + + if (!_dbus_string_init (&escaped)) + return NULL; + + if (!_dbus_address_append_escaped (&escaped, &unescaped)) + goto out; + + if (!_dbus_string_steal_data (&escaped, &ret)) + goto out; + + out: + _dbus_string_free (&escaped); + return ret; +} + +/** + * Unescapes the given string as a value in a key=value pair + * for a D-Bus address. Note that dbus_address_entry_get_value() + * returns an already-unescaped value. + * + * @param value the escaped value + * @param error error to set if the unescaping fails + * @returns newly-allocated unescaped value or #NULL if no memory + */ +char* +dbus_address_unescape_value (const char *value, + DBusError *error) +{ + DBusString unescaped; + DBusString escaped; + char *ret; + + ret = NULL; + + _dbus_string_init_const (&escaped, value); + + if (!_dbus_string_init (&unescaped)) + return NULL; + + if (!append_unescaped_value (&unescaped, &escaped, + 0, _dbus_string_get_length (&escaped), + error)) + goto out; + + if (!_dbus_string_steal_data (&unescaped, &ret)) + goto out; + + out: + if (ret == NULL && error && !dbus_error_is_set (error)) + _DBUS_SET_OOM (error); + + _dbus_assert (ret != NULL || error == NULL || dbus_error_is_set (error)); + + _dbus_string_free (&unescaped); + return ret; +} + +/** @} */ /* End of public API */ + +#ifdef DBUS_BUILD_TESTS + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +#include "dbus-test.h" +#include + +typedef struct +{ + const char *escaped; + const char *unescaped; +} EscapeTest; + +static const EscapeTest escape_tests[] = { + { "abcde", "abcde" }, + { "", "" }, + { "%20%20", " " }, + { "%24", "$" }, + { "%25", "%" }, + { "abc%24", "abc$" }, + { "%24abc", "$abc" }, + { "abc%24abc", "abc$abc" }, + { "/", "/" }, + { "-", "-" }, + { "_", "_" }, + { "A", "A" }, + { "I", "I" }, + { "Z", "Z" }, + { "a", "a" }, + { "i", "i" }, + { "z", "z" } +}; + +static const char* invalid_escaped_values[] = { + "%a", + "%q", + "%az", + "%%", + "%$$", + "abc%a", + "%axyz", + "%", + "$", + " ", +}; + +dbus_bool_t +_dbus_address_test (void) +{ + DBusAddressEntry **entries; + int len; + DBusError error = DBUS_ERROR_INIT; + int i; + + i = 0; + while (i < _DBUS_N_ELEMENTS (escape_tests)) + { + const EscapeTest *test = &escape_tests[i]; + char *escaped; + char *unescaped; + + escaped = dbus_address_escape_value (test->unescaped); + if (escaped == NULL) + _dbus_assert_not_reached ("oom"); + + if (strcmp (escaped, test->escaped) != 0) + { + _dbus_warn ("Escaped '%s' as '%s' should have been '%s'\n", + test->unescaped, escaped, test->escaped); + exit (1); + } + dbus_free (escaped); + + unescaped = dbus_address_unescape_value (test->escaped, &error); + if (unescaped == NULL) + { + _dbus_warn ("Failed to unescape '%s': %s\n", + test->escaped, error.message); + dbus_error_free (&error); + exit (1); + } + + if (strcmp (unescaped, test->unescaped) != 0) + { + _dbus_warn ("Unescaped '%s' as '%s' should have been '%s'\n", + test->escaped, unescaped, test->unescaped); + exit (1); + } + dbus_free (unescaped); + + ++i; + } + + i = 0; + while (i < _DBUS_N_ELEMENTS (invalid_escaped_values)) + { + char *unescaped; + + unescaped = dbus_address_unescape_value (invalid_escaped_values[i], + &error); + if (unescaped != NULL) + { + _dbus_warn ("Should not have successfully unescaped '%s' to '%s'\n", + invalid_escaped_values[i], unescaped); + dbus_free (unescaped); + exit (1); + } + + _dbus_assert (dbus_error_is_set (&error)); + dbus_error_free (&error); + + ++i; + } + + if (!dbus_parse_address ("unix:path=/tmp/foo;debug:name=test,sliff=sloff;", + &entries, &len, &error)) + _dbus_assert_not_reached ("could not parse address"); + _dbus_assert (len == 2); + _dbus_assert (strcmp (dbus_address_entry_get_value (entries[0], "path"), "/tmp/foo") == 0); + _dbus_assert (strcmp (dbus_address_entry_get_value (entries[1], "name"), "test") == 0); + _dbus_assert (strcmp (dbus_address_entry_get_value (entries[1], "sliff"), "sloff") == 0); + + dbus_address_entries_free (entries); + + /* Different possible errors */ + if (dbus_parse_address ("", &entries, &len, &error)) + _dbus_assert_not_reached ("Parsed incorrect address."); + else + dbus_error_free (&error); + + if (dbus_parse_address ("foo", &entries, &len, &error)) + _dbus_assert_not_reached ("Parsed incorrect address."); + else + dbus_error_free (&error); + + if (dbus_parse_address ("foo:bar", &entries, &len, &error)) + _dbus_assert_not_reached ("Parsed incorrect address."); + else + dbus_error_free (&error); + + if (dbus_parse_address ("foo:bar,baz", &entries, &len, &error)) + _dbus_assert_not_reached ("Parsed incorrect address."); + else + dbus_error_free (&error); + + if (dbus_parse_address ("foo:bar=foo,baz", &entries, &len, &error)) + _dbus_assert_not_reached ("Parsed incorrect address."); + else + dbus_error_free (&error); + + if (dbus_parse_address ("foo:bar=foo;baz", &entries, &len, &error)) + _dbus_assert_not_reached ("Parsed incorrect address."); + else + dbus_error_free (&error); + + if (dbus_parse_address ("foo:=foo", &entries, &len, &error)) + _dbus_assert_not_reached ("Parsed incorrect address."); + else + dbus_error_free (&error); + + if (dbus_parse_address ("foo:foo=", &entries, &len, &error)) + _dbus_assert_not_reached ("Parsed incorrect address."); + else + dbus_error_free (&error); + + if (dbus_parse_address ("foo:foo,bar=baz", &entries, &len, &error)) + _dbus_assert_not_reached ("Parsed incorrect address."); + else + dbus_error_free (&error); + + return TRUE; +} + +#endif /* !DOXYGEN_SHOULD_SKIP_THIS */ + +#endif diff --git a/src/dbus/dbus-address.h b/src/dbus/dbus-address.h new file mode 100644 index 0000000..c569044 --- /dev/null +++ b/src/dbus/dbus-address.h @@ -0,0 +1,61 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-address.h Server address 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION) +#error "Only can be included directly, this file may disappear or change contents." +#endif + +#ifndef DBUS_ADDRESS_H +#define DBUS_ADDRESS_H + +#include +#include + +DBUS_BEGIN_DECLS + +/** + * @addtogroup DBusAddress + * @{ + */ + +/** Opaque type representing one of the semicolon-separated items in an address */ +typedef struct DBusAddressEntry DBusAddressEntry; + +dbus_bool_t dbus_parse_address (const char *address, + DBusAddressEntry ***entry, + int *array_len, + DBusError *error); +const char *dbus_address_entry_get_value (DBusAddressEntry *entry, + const char *key); +const char *dbus_address_entry_get_method (DBusAddressEntry *entry); +void dbus_address_entries_free (DBusAddressEntry **entries); + +char* dbus_address_escape_value (const char *value); +char* dbus_address_unescape_value (const char *value, + DBusError *error); + +/** @} */ + +DBUS_END_DECLS + +#endif /* DBUS_ADDRESS_H */ + diff --git a/src/dbus/dbus-arch-deps.h b/src/dbus/dbus-arch-deps.h new file mode 100644 index 0000000..c526a2c --- /dev/null +++ b/src/dbus/dbus-arch-deps.h @@ -0,0 +1,67 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-arch-deps.h Header with architecture/compiler specific information, installed to libdir + * + * Copyright (C) 2003 Red Hat, Inc. + * + * Licensed under the Academic Free License version 2.0 + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION) +#error "Only can be included directly, this file may disappear or change contents." +#endif + +#ifndef DBUS_ARCH_DEPS_H +#define DBUS_ARCH_DEPS_H + +#include + +DBUS_BEGIN_DECLS + +#if 1 +#define DBUS_HAVE_INT64 1 +_DBUS_GNUC_EXTENSION typedef long dbus_int64_t; +_DBUS_GNUC_EXTENSION typedef unsigned long dbus_uint64_t; + +#define DBUS_INT64_CONSTANT(val) (_DBUS_GNUC_EXTENSION (val##L)) +#define DBUS_UINT64_CONSTANT(val) (_DBUS_GNUC_EXTENSION (val##UL)) + +#else +#undef DBUS_HAVE_INT64 +#undef DBUS_INT64_CONSTANT +#undef DBUS_UINT64_CONSTANT +#endif + +typedef int dbus_int32_t; +typedef unsigned int dbus_uint32_t; + +typedef short dbus_int16_t; +typedef unsigned short dbus_uint16_t; + +/* This is not really arch-dependent, but it's not worth + * creating an additional generated header just for this + */ +#define DBUS_MAJOR_VERSION 1 +#define DBUS_MINOR_VERSION 2 +#define DBUS_MICRO_VERSION 14 + +#define DBUS_VERSION_STRING "1.2.14" + +#define DBUS_VERSION ((1 << 16) | (2 << 8) | (14)) + +DBUS_END_DECLS + +#endif /* DBUS_ARCH_DEPS_H */ diff --git a/src/dbus/dbus-arch-deps.h.in b/src/dbus/dbus-arch-deps.h.in new file mode 100644 index 0000000..ca8e286 --- /dev/null +++ b/src/dbus/dbus-arch-deps.h.in @@ -0,0 +1,67 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-arch-deps.h Header with architecture/compiler specific information, installed to libdir + * + * Copyright (C) 2003 Red Hat, Inc. + * + * Licensed under the Academic Free License version 2.0 + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION) +#error "Only can be included directly, this file may disappear or change contents." +#endif + +#ifndef DBUS_ARCH_DEPS_H +#define DBUS_ARCH_DEPS_H + +#include + +DBUS_BEGIN_DECLS + +#if @DBUS_HAVE_INT64@ +#define DBUS_HAVE_INT64 1 +_DBUS_GNUC_EXTENSION typedef @DBUS_INT64_TYPE@ dbus_int64_t; +_DBUS_GNUC_EXTENSION typedef unsigned @DBUS_INT64_TYPE@ dbus_uint64_t; + +#define DBUS_INT64_CONSTANT(val) (_DBUS_GNUC_EXTENSION @DBUS_INT64_CONSTANT@) +#define DBUS_UINT64_CONSTANT(val) (_DBUS_GNUC_EXTENSION @DBUS_UINT64_CONSTANT@) + +#else +#undef DBUS_HAVE_INT64 +#undef DBUS_INT64_CONSTANT +#undef DBUS_UINT64_CONSTANT +#endif + +typedef @DBUS_INT32_TYPE@ dbus_int32_t; +typedef unsigned @DBUS_INT32_TYPE@ dbus_uint32_t; + +typedef @DBUS_INT16_TYPE@ dbus_int16_t; +typedef unsigned @DBUS_INT16_TYPE@ dbus_uint16_t; + +/* This is not really arch-dependent, but it's not worth + * creating an additional generated header just for this + */ +#define DBUS_MAJOR_VERSION @DBUS_MAJOR_VERSION@ +#define DBUS_MINOR_VERSION @DBUS_MINOR_VERSION@ +#define DBUS_MICRO_VERSION @DBUS_MICRO_VERSION@ + +#define DBUS_VERSION_STRING "@DBUS_VERSION@" + +#define DBUS_VERSION ((@DBUS_MAJOR_VERSION@ << 16) | (@DBUS_MINOR_VERSION@ << 8) | (@DBUS_MICRO_VERSION@)) + +DBUS_END_DECLS + +#endif /* DBUS_ARCH_DEPS_H */ diff --git a/src/dbus/dbus-auth-script.c b/src/dbus/dbus-auth-script.c new file mode 100644 index 0000000..93d6f78 --- /dev/null +++ b/src/dbus/dbus-auth-script.c @@ -0,0 +1,803 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-auth-script.c Test DBusAuth using a special script file (internal to D-Bus 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include + +#ifdef DBUS_BUILD_TESTS + +#include "dbus-auth-script.h" +#include "dbus-auth.h" +#include "dbus-string.h" +#include "dbus-hash.h" +#include "dbus-credentials.h" +#include "dbus-internals.h" + +/** + * @defgroup DBusAuthScript code for running unit test scripts for DBusAuth + * @ingroup DBusInternals + * @brief DBusAuth unit test scripting + * + * The code in here is used for unit testing, it loads + * up a script that tests DBusAuth. + * + * @{ + */ + +/* this is slightly different from the other append_quoted_string + * in dbus-message-builder.c + */ +static dbus_bool_t +append_quoted_string (DBusString *dest, + const DBusString *quoted) +{ + dbus_bool_t in_quotes = FALSE; + dbus_bool_t in_backslash = FALSE; + int i; + + i = 0; + while (i < _dbus_string_get_length (quoted)) + { + unsigned char b; + + b = _dbus_string_get_byte (quoted, i); + + if (in_backslash) + { + unsigned char a; + + if (b == 'r') + a = '\r'; + else if (b == 'n') + a = '\n'; + else if (b == '\\') + a = '\\'; + else + { + _dbus_warn ("bad backslashed byte %c\n", b); + return FALSE; + } + + if (!_dbus_string_append_byte (dest, a)) + return FALSE; + + in_backslash = FALSE; + } + else if (b == '\\') + { + in_backslash = TRUE; + } + else if (in_quotes) + { + if (b == '\'') + in_quotes = FALSE; + else + { + if (!_dbus_string_append_byte (dest, b)) + return FALSE; + } + } + else + { + if (b == '\'') + in_quotes = TRUE; + else if (b == ' ' || b == '\n' || b == '\t') + break; /* end on whitespace if not quoted */ + else + { + if (!_dbus_string_append_byte (dest, b)) + return FALSE; + } + } + + ++i; + } + + return TRUE; +} + +static dbus_bool_t +same_first_word (const DBusString *a, + const DBusString *b) +{ + int first_a_blank, first_b_blank; + + _dbus_string_find_blank (a, 0, &first_a_blank); + _dbus_string_find_blank (b, 0, &first_b_blank); + + if (first_a_blank != first_b_blank) + return FALSE; + + return _dbus_string_equal_len (a, b, first_a_blank); +} + +static DBusAuthState +auth_state_from_string (const DBusString *str) +{ + if (_dbus_string_starts_with_c_str (str, "WAITING_FOR_INPUT")) + return DBUS_AUTH_STATE_WAITING_FOR_INPUT; + else if (_dbus_string_starts_with_c_str (str, "WAITING_FOR_MEMORY")) + return DBUS_AUTH_STATE_WAITING_FOR_MEMORY; + else if (_dbus_string_starts_with_c_str (str, "HAVE_BYTES_TO_SEND")) + return DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND; + else if (_dbus_string_starts_with_c_str (str, "NEED_DISCONNECT")) + return DBUS_AUTH_STATE_NEED_DISCONNECT; + else if (_dbus_string_starts_with_c_str (str, "AUTHENTICATED")) + return DBUS_AUTH_STATE_AUTHENTICATED; + else + return -1; +} + +static const char* +auth_state_to_string (DBusAuthState state) +{ + switch (state) + { + case DBUS_AUTH_STATE_WAITING_FOR_INPUT: + return "WAITING_FOR_INPUT"; + case DBUS_AUTH_STATE_WAITING_FOR_MEMORY: + return "WAITING_FOR_MEMORY"; + case DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND: + return "HAVE_BYTES_TO_SEND"; + case DBUS_AUTH_STATE_NEED_DISCONNECT: + return "NEED_DISCONNECT"; + case DBUS_AUTH_STATE_AUTHENTICATED: + return "AUTHENTICATED"; + } + + return "unknown"; +} + +static char ** +split_string (DBusString *str) +{ + int i, j, k, count, end; + char **array; + + end = _dbus_string_get_length (str); + + i = 0; + _dbus_string_skip_blank (str, i, &i); + for (count = 0; i < end; count++) + { + _dbus_string_find_blank (str, i, &i); + _dbus_string_skip_blank (str, i, &i); + } + + array = dbus_new0 (char *, count + 1); + if (array == NULL) + return NULL; + + i = 0; + _dbus_string_skip_blank (str, i, &i); + for (k = 0; k < count; k++) + { + _dbus_string_find_blank (str, i, &j); + + array[k] = dbus_malloc (j - i + 1); + if (array[k] == NULL) + { + dbus_free_string_array (array); + return NULL; + } + memcpy (array[k], + _dbus_string_get_const_data_len (str, i, j - i), j - i); + array[k][j - i] = '\0'; + + _dbus_string_skip_blank (str, j, &i); + } + array[k] = NULL; + + return array; +} + +static void +auth_set_unix_credentials(DBusAuth *auth, + dbus_uid_t uid, + dbus_pid_t pid) +{ + DBusCredentials *credentials; + + credentials = _dbus_credentials_new (); + if (credentials == NULL) + _dbus_assert_not_reached ("no memory"); + + if (uid != DBUS_UID_UNSET) + _dbus_credentials_add_unix_uid (credentials, uid); + if (pid != DBUS_PID_UNSET) + _dbus_credentials_add_unix_pid (credentials, pid); + + _dbus_auth_set_credentials (auth, credentials); + + _dbus_credentials_unref (credentials); +} + +/** + * Runs an "auth script" which is a script for testing the + * authentication protocol. Scripts send and receive data, and then + * include assertions about the state of both ends of the connection + * after processing the data. A script succeeds if these assertions + * hold. + * + * @param filename the file containing the script to run + * @returns #TRUE if the script succeeds, #FALSE otherwise + */ +dbus_bool_t +_dbus_auth_script_run (const DBusString *filename) +{ + DBusString file; + DBusError error = DBUS_ERROR_INIT; + DBusString line; + dbus_bool_t retval; + int line_no; + DBusAuth *auth; + DBusString from_auth; + DBusAuthState state; + DBusString context; + DBusString guid; + + retval = FALSE; + auth = NULL; + + _dbus_string_init_const (&guid, "5fa01f4202cd837709a3274ca0df9d00"); + _dbus_string_init_const (&context, "org_freedesktop_test"); + + if (!_dbus_string_init (&file)) + return FALSE; + + if (!_dbus_string_init (&line)) + { + _dbus_string_free (&file); + return FALSE; + } + + if (!_dbus_string_init (&from_auth)) + { + _dbus_string_free (&file); + _dbus_string_free (&line); + return FALSE; + } + + if (!_dbus_file_get_contents (&file, filename, &error)) { + _dbus_warn ("Getting contents of %s failed: %s\n", + _dbus_string_get_const_data (filename), error.message); + dbus_error_free (&error); + goto out; + } + + state = DBUS_AUTH_STATE_NEED_DISCONNECT; + line_no = 0; + + next_iteration: + while (_dbus_string_pop_line (&file, &line)) + { + line_no += 1; + + /* _dbus_warn ("%s\n", _dbus_string_get_const_data (&line)); */ + + _dbus_string_delete_leading_blanks (&line); + + if (auth != NULL) + { + while ((state = _dbus_auth_do_work (auth)) == + DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND) + { + const DBusString *tmp; + if (_dbus_auth_get_bytes_to_send (auth, &tmp)) + { + int count = _dbus_string_get_length (tmp); + + if (_dbus_string_copy (tmp, 0, &from_auth, + _dbus_string_get_length (&from_auth))) + _dbus_auth_bytes_sent (auth, count); + } + } + } + + if (_dbus_string_get_length (&line) == 0) + { + /* empty line */ + goto next_iteration; + } + else if (_dbus_string_starts_with_c_str (&line, + "#")) + { + /* Ignore this comment */ + goto next_iteration; + } +#ifdef DBUS_WIN + else if (_dbus_string_starts_with_c_str (&line, + "WIN_ONLY")) + { + /* Ignore this line */ + goto next_iteration; + } + else if (_dbus_string_starts_with_c_str (&line, + "UNIX_ONLY")) + { + /* skip this file */ + _dbus_warn ("skipping unix only auth script\n"); + retval = TRUE; + goto out; + } +#endif +#ifdef DBUS_UNIX + else if (_dbus_string_starts_with_c_str (&line, + "UNIX_ONLY")) + { + /* Ignore this line */ + goto next_iteration; + } + else if (_dbus_string_starts_with_c_str (&line, + "WIN_ONLY")) + { + /* skip this file */ + _dbus_warn ("skipping windows only auth script\n"); + retval = TRUE; + goto out; + } +#endif + else if (_dbus_string_starts_with_c_str (&line, + "CLIENT")) + { + DBusCredentials *creds; + + if (auth != NULL) + { + _dbus_warn ("already created a DBusAuth (CLIENT or SERVER given twice)\n"); + goto out; + } + + auth = _dbus_auth_client_new (); + if (auth == NULL) + { + _dbus_warn ("no memory to create DBusAuth\n"); + goto out; + } + + /* test ref/unref */ + _dbus_auth_ref (auth); + _dbus_auth_unref (auth); + + creds = _dbus_credentials_new_from_current_process (); + if (creds == NULL) + { + _dbus_warn ("no memory for credentials\n"); + _dbus_auth_unref (auth); + auth = NULL; + goto out; + } + + if (!_dbus_auth_set_credentials (auth, creds)) + { + _dbus_warn ("no memory for setting credentials\n"); + _dbus_auth_unref (auth); + auth = NULL; + _dbus_credentials_unref (creds); + goto out; + } + + _dbus_credentials_unref (creds); + } + else if (_dbus_string_starts_with_c_str (&line, + "SERVER")) + { + DBusCredentials *creds; + + if (auth != NULL) + { + _dbus_warn ("already created a DBusAuth (CLIENT or SERVER given twice)\n"); + goto out; + } + + auth = _dbus_auth_server_new (&guid); + if (auth == NULL) + { + _dbus_warn ("no memory to create DBusAuth\n"); + goto out; + } + + /* test ref/unref */ + _dbus_auth_ref (auth); + _dbus_auth_unref (auth); + + creds = _dbus_credentials_new_from_current_process (); + if (creds == NULL) + { + _dbus_warn ("no memory for credentials\n"); + _dbus_auth_unref (auth); + auth = NULL; + goto out; + } + + if (!_dbus_auth_set_credentials (auth, creds)) + { + _dbus_warn ("no memory for setting credentials\n"); + _dbus_auth_unref (auth); + auth = NULL; + _dbus_credentials_unref (creds); + goto out; + } + + _dbus_credentials_unref (creds); + + _dbus_auth_set_context (auth, &context); + } + else if (auth == NULL) + { + _dbus_warn ("must specify CLIENT or SERVER\n"); + goto out; + + } + else if (_dbus_string_starts_with_c_str (&line, + "NO_CREDENTIALS")) + { + auth_set_unix_credentials (auth, DBUS_UID_UNSET, DBUS_PID_UNSET); + } + else if (_dbus_string_starts_with_c_str (&line, + "ROOT_CREDENTIALS")) + { + auth_set_unix_credentials (auth, 0, DBUS_PID_UNSET); + } + else if (_dbus_string_starts_with_c_str (&line, + "SILLY_CREDENTIALS")) + { + auth_set_unix_credentials (auth, 4312, DBUS_PID_UNSET); + } + else if (_dbus_string_starts_with_c_str (&line, + "ALLOWED_MECHS")) + { + char **mechs; + + _dbus_string_delete_first_word (&line); + mechs = split_string (&line); + _dbus_auth_set_mechanisms (auth, (const char **) mechs); + dbus_free_string_array (mechs); + } + else if (_dbus_string_starts_with_c_str (&line, + "SEND")) + { + DBusString to_send; + + _dbus_string_delete_first_word (&line); + + if (!_dbus_string_init (&to_send)) + { + _dbus_warn ("no memory to allocate string\n"); + goto out; + } + + if (!append_quoted_string (&to_send, &line)) + { + _dbus_warn ("failed to append quoted string line %d\n", + line_no); + _dbus_string_free (&to_send); + goto out; + } + + _dbus_verbose ("Sending '%s'\n", _dbus_string_get_const_data (&to_send)); + + if (!_dbus_string_append (&to_send, "\r\n")) + { + _dbus_warn ("failed to append \r\n from line %d\n", + line_no); + _dbus_string_free (&to_send); + goto out; + } + + /* Replace USERID_HEX with our username in hex */ + { + int where; + + if (_dbus_string_find (&to_send, 0, + "USERID_HEX", &where)) + { + DBusString username; + + if (!_dbus_string_init (&username)) + { + _dbus_warn ("no memory for userid\n"); + _dbus_string_free (&to_send); + goto out; + } + + if (!_dbus_append_user_from_current_process (&username)) + { + _dbus_warn ("no memory for userid\n"); + _dbus_string_free (&username); + _dbus_string_free (&to_send); + goto out; + } + + _dbus_string_delete (&to_send, where, strlen ("USERID_HEX")); + + if (!_dbus_string_hex_encode (&username, 0, + &to_send, where)) + { + _dbus_warn ("no memory to subst USERID_HEX\n"); + _dbus_string_free (&username); + _dbus_string_free (&to_send); + goto out; + } + + _dbus_string_free (&username); + } + else if (_dbus_string_find (&to_send, 0, + "USERNAME_HEX", &where)) + { + DBusString username; + + if (!_dbus_string_init (&username)) + { + _dbus_warn ("no memory for username\n"); + _dbus_string_free (&to_send); + goto out; + } + + if (!_dbus_append_user_from_current_process (&username)) + { + _dbus_warn ("no memory for username\n"); + _dbus_string_free (&username); + _dbus_string_free (&to_send); + goto out; + } + + _dbus_string_delete (&to_send, where, strlen ("USERNAME_HEX")); + + if (!_dbus_string_hex_encode (&username, 0, + &to_send, where)) + { + _dbus_warn ("no memory to subst USERNAME_HEX\n"); + _dbus_string_free (&username); + _dbus_string_free (&to_send); + goto out; + } + + _dbus_string_free (&username); + } + } + + { + DBusString *buffer; + + _dbus_auth_get_buffer (auth, &buffer); + if (!_dbus_string_copy (&to_send, 0, + buffer, _dbus_string_get_length (buffer))) + { + _dbus_warn ("not enough memory to call bytes_received, or can't add bytes to auth object already in end state\n"); + _dbus_string_free (&to_send); + _dbus_auth_return_buffer (auth, buffer, 0); + goto out; + } + + _dbus_auth_return_buffer (auth, buffer, _dbus_string_get_length (&to_send)); + } + + _dbus_string_free (&to_send); + } + else if (_dbus_string_starts_with_c_str (&line, + "EXPECT_STATE")) + { + DBusAuthState expected; + + _dbus_string_delete_first_word (&line); + + expected = auth_state_from_string (&line); + if (expected < 0) + { + _dbus_warn ("bad auth state given to EXPECT_STATE\n"); + goto parse_failed; + } + + if (expected != state) + { + _dbus_warn ("expected auth state %s but got %s on line %d\n", + auth_state_to_string (expected), + auth_state_to_string (state), + line_no); + goto out; + } + } + else if (_dbus_string_starts_with_c_str (&line, + "EXPECT_COMMAND")) + { + DBusString received; + + _dbus_string_delete_first_word (&line); + + if (!_dbus_string_init (&received)) + { + _dbus_warn ("no mem to allocate string received\n"); + goto out; + } + + if (!_dbus_string_pop_line (&from_auth, &received)) + { + _dbus_warn ("no line popped from the DBusAuth being tested, expected command %s on line %d\n", + _dbus_string_get_const_data (&line), line_no); + _dbus_string_free (&received); + goto out; + } + + if (!same_first_word (&received, &line)) + { + _dbus_warn ("line %d expected command '%s' and got '%s'\n", + line_no, + _dbus_string_get_const_data (&line), + _dbus_string_get_const_data (&received)); + _dbus_string_free (&received); + goto out; + } + + _dbus_string_free (&received); + } + else if (_dbus_string_starts_with_c_str (&line, + "EXPECT_UNUSED")) + { + DBusString expected; + const DBusString *unused; + + _dbus_string_delete_first_word (&line); + + if (!_dbus_string_init (&expected)) + { + _dbus_warn ("no mem to allocate string expected\n"); + goto out; + } + + if (!append_quoted_string (&expected, &line)) + { + _dbus_warn ("failed to append quoted string line %d\n", + line_no); + _dbus_string_free (&expected); + goto out; + } + + _dbus_auth_get_unused_bytes (auth, &unused); + + if (_dbus_string_equal (&expected, unused)) + { + _dbus_auth_delete_unused_bytes (auth); + _dbus_string_free (&expected); + } + else + { + _dbus_warn ("Expected unused bytes '%s' and have '%s'\n", + _dbus_string_get_const_data (&expected), + _dbus_string_get_const_data (unused)); + _dbus_string_free (&expected); + goto out; + } + } + else if (_dbus_string_starts_with_c_str (&line, + "EXPECT_HAVE_NO_CREDENTIALS")) + { + DBusCredentials *authorized_identity; + + authorized_identity = _dbus_auth_get_identity (auth); + if (!_dbus_credentials_are_anonymous (authorized_identity)) + { + _dbus_warn ("Expected anonymous login or failed login, but some credentials were authorized\n"); + goto out; + } + } + else if (_dbus_string_starts_with_c_str (&line, + "EXPECT_HAVE_SOME_CREDENTIALS")) + { + DBusCredentials *authorized_identity; + + authorized_identity = _dbus_auth_get_identity (auth); + if (_dbus_credentials_are_anonymous (authorized_identity)) + { + _dbus_warn ("Expected to have some credentials, but we don't\n"); + goto out; + } + } + else if (_dbus_string_starts_with_c_str (&line, + "EXPECT")) + { + DBusString expected; + + _dbus_string_delete_first_word (&line); + + if (!_dbus_string_init (&expected)) + { + _dbus_warn ("no mem to allocate string expected\n"); + goto out; + } + + if (!append_quoted_string (&expected, &line)) + { + _dbus_warn ("failed to append quoted string line %d\n", + line_no); + _dbus_string_free (&expected); + goto out; + } + + if (_dbus_string_equal_len (&expected, &from_auth, + _dbus_string_get_length (&expected))) + { + _dbus_string_delete (&from_auth, 0, + _dbus_string_get_length (&expected)); + _dbus_string_free (&expected); + } + else + { + _dbus_warn ("Expected exact string '%s' and have '%s'\n", + _dbus_string_get_const_data (&expected), + _dbus_string_get_const_data (&from_auth)); + _dbus_string_free (&expected); + goto out; + } + } + else + goto parse_failed; + + goto next_iteration; /* skip parse_failed */ + + parse_failed: + { + _dbus_warn ("couldn't process line %d \"%s\"\n", + line_no, _dbus_string_get_const_data (&line)); + goto out; + } + } + + if (auth == NULL) + { + _dbus_warn ("Auth script is bogus, did not even have CLIENT or SERVER\n"); + goto out; + } + else if (state == DBUS_AUTH_STATE_AUTHENTICATED) + { + const DBusString *unused; + + _dbus_auth_get_unused_bytes (auth, &unused); + + if (_dbus_string_get_length (unused) > 0) + { + _dbus_warn ("did not expect unused bytes (scripts must specify explicitly if they are expected)\n"); + goto out; + } + } + + if (_dbus_string_get_length (&from_auth) > 0) + { + _dbus_warn ("script did not have EXPECT_ statements for all the data received from the DBusAuth\n"); + _dbus_warn ("Leftover data: %s\n", _dbus_string_get_const_data (&from_auth)); + goto out; + } + + retval = TRUE; + + out: + if (auth) + _dbus_auth_unref (auth); + + _dbus_string_free (&file); + _dbus_string_free (&line); + _dbus_string_free (&from_auth); + + return retval; +} + +/** @} */ +#endif /* DBUS_BUILD_TESTS */ diff --git a/src/dbus/dbus-auth-script.h b/src/dbus/dbus-auth-script.h new file mode 100644 index 0000000..1f4da1b --- /dev/null +++ b/src/dbus/dbus-auth-script.h @@ -0,0 +1,39 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-auth-script.h Test DBusAuth using a special script file (internal to D-Bus 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef DBUS_AUTH_SCRIPT_H +#define DBUS_AUTH_SCRIPT_H + +#include + +#include +#include +#include + +DBUS_BEGIN_DECLS + +dbus_bool_t _dbus_auth_script_run (const DBusString *filename); + +DBUS_END_DECLS + +#endif /* DBUS_AUTH_SCRIPT_H */ diff --git a/src/dbus/dbus-auth-util.c b/src/dbus/dbus-auth-util.c new file mode 100644 index 0000000..e501904 --- /dev/null +++ b/src/dbus/dbus-auth-util.c @@ -0,0 +1,168 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-auth-util.c Would be in dbus-auth.c, but only used for tests/bus + * + * Copyright (C) 2002, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include "dbus-internals.h" +#include "dbus-test.h" +#include "dbus-auth.h" + +/** + * @addtogroup DBusAuth + * @{ + */ + +/** @} */ + +#ifdef DBUS_BUILD_TESTS +#include "dbus-test.h" +#include "dbus-auth-script.h" +#include + +static dbus_bool_t +process_test_subdir (const DBusString *test_base_dir, + const char *subdir) +{ + DBusString test_directory; + DBusString filename; + DBusDirIter *dir; + dbus_bool_t retval; + DBusError error = DBUS_ERROR_INIT; + + 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"); + + 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; + } + + printf ("Testing %s:\n", subdir); + + 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 (&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 (&filename, ".auth-script")) + { + _dbus_verbose ("Skipping non-.auth-script 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)); + + if (!_dbus_auth_script_run (&full_path)) + { + _dbus_string_free (&full_path); + goto failed; + } + else + _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 +process_test_dirs (const char *test_data_dir) +{ + DBusString test_directory; + dbus_bool_t retval; + + retval = FALSE; + + _dbus_string_init_const (&test_directory, test_data_dir); + + if (!process_test_subdir (&test_directory, "auth")) + goto failed; + + retval = TRUE; + + failed: + + _dbus_string_free (&test_directory); + + return retval; +} + +dbus_bool_t +_dbus_auth_test (const char *test_data_dir) +{ + + if (test_data_dir == NULL) + return TRUE; + + if (!process_test_dirs (test_data_dir)) + return FALSE; + + return TRUE; +} + +#endif /* DBUS_BUILD_TESTS */ diff --git a/src/dbus/dbus-auth.c b/src/dbus/dbus-auth.c new file mode 100644 index 0000000..ec7cf31 --- /dev/null +++ b/src/dbus/dbus-auth.c @@ -0,0 +1,2690 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-auth.c Authentication + * + * Copyright (C) 2002, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include "dbus-auth.h" +#include "dbus-string.h" +#include "dbus-list.h" +#include "dbus-internals.h" +#include "dbus-keyring.h" +#include "dbus-sha.h" +#include "dbus-protocol.h" +#include "dbus-credentials.h" + +/** + * @defgroup DBusAuth Authentication + * @ingroup DBusInternals + * @brief DBusAuth object + * + * DBusAuth manages the authentication negotiation when a connection + * is first established, and also manage any encryption used over a + * connection. + * + * @todo some SASL profiles require sending the empty string as a + * challenge/response, but we don't currently allow that in our + * protocol. + * + * @todo right now sometimes both ends will block waiting for input + * from the other end, e.g. if there's an error during + * DBUS_COOKIE_SHA1. + * + * @todo the cookie keyring needs to be cached globally not just + * per-auth (which raises threadsafety issues too) + * + * @todo grep FIXME in dbus-auth.c + */ + +/** + * @defgroup DBusAuthInternals Authentication implementation details + * @ingroup DBusInternals + * @brief DBusAuth implementation details + * + * Private details of authentication code. + * + * @{ + */ + +/** + * This function appends an initial client response to the given string + */ +typedef dbus_bool_t (* DBusInitialResponseFunction) (DBusAuth *auth, + DBusString *response); + +/** + * This function processes a block of data received from the peer. + * i.e. handles a DATA command. + */ +typedef dbus_bool_t (* DBusAuthDataFunction) (DBusAuth *auth, + const DBusString *data); + +/** + * This function encodes a block of data from the peer. + */ +typedef dbus_bool_t (* DBusAuthEncodeFunction) (DBusAuth *auth, + const DBusString *data, + DBusString *encoded); + +/** + * This function decodes a block of data from the peer. + */ +typedef dbus_bool_t (* DBusAuthDecodeFunction) (DBusAuth *auth, + const DBusString *data, + DBusString *decoded); + +/** + * This function is called when the mechanism is abandoned. + */ +typedef void (* DBusAuthShutdownFunction) (DBusAuth *auth); + +/** + * Virtual table representing a particular auth mechanism. + */ +typedef struct +{ + const char *mechanism; /**< Name of the mechanism */ + DBusAuthDataFunction server_data_func; /**< Function on server side for DATA */ + DBusAuthEncodeFunction server_encode_func; /**< Function on server side to encode */ + DBusAuthDecodeFunction server_decode_func; /**< Function on server side to decode */ + DBusAuthShutdownFunction server_shutdown_func; /**< Function on server side to shut down */ + DBusInitialResponseFunction client_initial_response_func; /**< Function on client side to handle initial response */ + DBusAuthDataFunction client_data_func; /**< Function on client side for DATA */ + DBusAuthEncodeFunction client_encode_func; /**< Function on client side for encode */ + DBusAuthDecodeFunction client_decode_func; /**< Function on client side for decode */ + DBusAuthShutdownFunction client_shutdown_func; /**< Function on client side for shutdown */ +} DBusAuthMechanismHandler; + +/** + * Enumeration for the known authentication commands. + */ +typedef enum { + DBUS_AUTH_COMMAND_AUTH, + DBUS_AUTH_COMMAND_CANCEL, + DBUS_AUTH_COMMAND_DATA, + DBUS_AUTH_COMMAND_BEGIN, + DBUS_AUTH_COMMAND_REJECTED, + DBUS_AUTH_COMMAND_OK, + DBUS_AUTH_COMMAND_ERROR, + DBUS_AUTH_COMMAND_UNKNOWN +} DBusAuthCommand; + +/** + * Auth state function, determines the reaction to incoming events for + * a particular state. Returns whether we had enough memory to + * complete the operation. + */ +typedef dbus_bool_t (* DBusAuthStateFunction) (DBusAuth *auth, + DBusAuthCommand command, + const DBusString *args); + +/** + * Information about a auth state. + */ +typedef struct +{ + const char *name; /**< Name of the state */ + DBusAuthStateFunction handler; /**< State function for this state */ +} DBusAuthStateData; + +/** + * Internal members of DBusAuth. + */ +struct DBusAuth +{ + int refcount; /**< reference count */ + const char *side; /**< Client or server */ + + DBusString incoming; /**< Incoming data buffer */ + DBusString outgoing; /**< Outgoing data buffer */ + + const DBusAuthStateData *state; /**< Current protocol state */ + + const DBusAuthMechanismHandler *mech; /**< Current auth mechanism */ + + DBusString identity; /**< Current identity we're authorizing + * as. + */ + + DBusCredentials *credentials; /**< Credentials read from socket + */ + + DBusCredentials *authorized_identity; /**< Credentials that are authorized */ + + DBusCredentials *desired_identity; /**< Identity client has requested */ + + DBusString context; /**< Cookie scope */ + DBusKeyring *keyring; /**< Keyring for cookie mechanism. */ + int cookie_id; /**< ID of cookie to use */ + DBusString challenge; /**< Challenge sent to client */ + + char **allowed_mechs; /**< Mechanisms we're allowed to use, + * or #NULL if we can use any + */ + + unsigned int needed_memory : 1; /**< We needed memory to continue since last + * successful getting something done + */ + unsigned int already_got_mechanisms : 1; /**< Client already got mech list */ + unsigned int already_asked_for_initial_response : 1; /**< Already sent a blank challenge to get an initial response */ + unsigned int buffer_outstanding : 1; /**< Buffer is "checked out" for reading data into */ +}; + +/** + * "Subclass" of DBusAuth for client side + */ +typedef struct +{ + DBusAuth base; /**< Parent class */ + + DBusList *mechs_to_try; /**< Mechanisms we got from the server that we're going to try using */ + + DBusString guid_from_server; /**< GUID received from server */ + +} DBusAuthClient; + +/** + * "Subclass" of DBusAuth for server side. + */ +typedef struct +{ + DBusAuth base; /**< Parent class */ + + int failures; /**< Number of times client has been rejected */ + int max_failures; /**< Number of times we reject before disconnect */ + + DBusString guid; /**< Our globally unique ID in hex encoding */ + +} DBusAuthServer; + +static void goto_state (DBusAuth *auth, + const DBusAuthStateData *new_state); +static dbus_bool_t send_auth (DBusAuth *auth, + const DBusAuthMechanismHandler *mech); +static dbus_bool_t send_data (DBusAuth *auth, + DBusString *data); +static dbus_bool_t send_rejected (DBusAuth *auth); +static dbus_bool_t send_error (DBusAuth *auth, + const char *message); +static dbus_bool_t send_ok (DBusAuth *auth); +static dbus_bool_t send_begin (DBusAuth *auth, + const DBusString *args_from_ok); +static dbus_bool_t send_cancel (DBusAuth *auth); + +/** + * Client states + */ + +static dbus_bool_t handle_server_state_waiting_for_auth (DBusAuth *auth, + DBusAuthCommand command, + const DBusString *args); +static dbus_bool_t handle_server_state_waiting_for_data (DBusAuth *auth, + DBusAuthCommand command, + const DBusString *args); +static dbus_bool_t handle_server_state_waiting_for_begin (DBusAuth *auth, + DBusAuthCommand command, + const DBusString *args); + +static const DBusAuthStateData server_state_waiting_for_auth = { + "WaitingForAuth", handle_server_state_waiting_for_auth +}; +static const DBusAuthStateData server_state_waiting_for_data = { + "WaitingForData", handle_server_state_waiting_for_data +}; +static const DBusAuthStateData server_state_waiting_for_begin = { + "WaitingForBegin", handle_server_state_waiting_for_begin +}; + +/** + * Client states + */ + +static dbus_bool_t handle_client_state_waiting_for_data (DBusAuth *auth, + DBusAuthCommand command, + const DBusString *args); +static dbus_bool_t handle_client_state_waiting_for_ok (DBusAuth *auth, + DBusAuthCommand command, + const DBusString *args); +static dbus_bool_t handle_client_state_waiting_for_reject (DBusAuth *auth, + DBusAuthCommand command, + const DBusString *args); + +static const DBusAuthStateData client_state_need_send_auth = { + "NeedSendAuth", NULL +}; +static const DBusAuthStateData client_state_waiting_for_data = { + "WaitingForData", handle_client_state_waiting_for_data +}; +static const DBusAuthStateData client_state_waiting_for_ok = { + "WaitingForOK", handle_client_state_waiting_for_ok +}; +static const DBusAuthStateData client_state_waiting_for_reject = { + "WaitingForReject", handle_client_state_waiting_for_reject +}; + +/** + * Common terminal states. Terminal states have handler == NULL. + */ + +static const DBusAuthStateData common_state_authenticated = { + "Authenticated", NULL +}; + +static const DBusAuthStateData common_state_need_disconnect = { + "NeedDisconnect", NULL +}; + +static const char auth_side_client[] = "client"; +static const char auth_side_server[] = "server"; +/** + * @param auth the auth conversation + * @returns #TRUE if the conversation is the server side + */ +#define DBUS_AUTH_IS_SERVER(auth) ((auth)->side == auth_side_server) +/** + * @param auth the auth conversation + * @returns #TRUE if the conversation is the client side + */ +#define DBUS_AUTH_IS_CLIENT(auth) ((auth)->side == auth_side_client) +/** + * @param auth the auth conversation + * @returns auth cast to DBusAuthClient + */ +#define DBUS_AUTH_CLIENT(auth) ((DBusAuthClient*)(auth)) +/** + * @param auth the auth conversation + * @returns auth cast to DBusAuthServer + */ +#define DBUS_AUTH_SERVER(auth) ((DBusAuthServer*)(auth)) + +/** + * The name of the auth ("client" or "server") + * @param auth the auth conversation + * @returns a string + */ +#define DBUS_AUTH_NAME(auth) ((auth)->side) + +static DBusAuth* +_dbus_auth_new (int size) +{ + DBusAuth *auth; + + auth = dbus_malloc0 (size); + if (auth == NULL) + return NULL; + + auth->refcount = 1; + + auth->keyring = NULL; + auth->cookie_id = -1; + + /* note that we don't use the max string length feature, + * because you can't use that feature if you're going to + * try to recover from out-of-memory (it creates + * what looks like unrecoverable inability to alloc + * more space in the string). But we do handle + * overlong buffers in _dbus_auth_do_work(). + */ + + if (!_dbus_string_init (&auth->incoming)) + goto enomem_0; + + if (!_dbus_string_init (&auth->outgoing)) + goto enomem_1; + + if (!_dbus_string_init (&auth->identity)) + goto enomem_2; + + if (!_dbus_string_init (&auth->context)) + goto enomem_3; + + if (!_dbus_string_init (&auth->challenge)) + goto enomem_4; + + /* default context if none is specified */ + if (!_dbus_string_append (&auth->context, "org_freedesktop_general")) + goto enomem_5; + + auth->credentials = _dbus_credentials_new (); + if (auth->credentials == NULL) + goto enomem_6; + + auth->authorized_identity = _dbus_credentials_new (); + if (auth->authorized_identity == NULL) + goto enomem_7; + + auth->desired_identity = _dbus_credentials_new (); + if (auth->desired_identity == NULL) + goto enomem_8; + + return auth; + +#if 0 + enomem_9: + _dbus_credentials_unref (auth->desired_identity); +#endif + enomem_8: + _dbus_credentials_unref (auth->authorized_identity); + enomem_7: + _dbus_credentials_unref (auth->credentials); + enomem_6: + /* last alloc was an append to context, which is freed already below */ ; + enomem_5: + _dbus_string_free (&auth->challenge); + enomem_4: + _dbus_string_free (&auth->context); + enomem_3: + _dbus_string_free (&auth->identity); + enomem_2: + _dbus_string_free (&auth->outgoing); + enomem_1: + _dbus_string_free (&auth->incoming); + enomem_0: + dbus_free (auth); + return NULL; +} + +static void +shutdown_mech (DBusAuth *auth) +{ + /* Cancel any auth */ + auth->already_asked_for_initial_response = FALSE; + _dbus_string_set_length (&auth->identity, 0); + + _dbus_credentials_clear (auth->authorized_identity); + _dbus_credentials_clear (auth->desired_identity); + + if (auth->mech != NULL) + { + _dbus_verbose ("%s: Shutting down mechanism %s\n", + DBUS_AUTH_NAME (auth), auth->mech->mechanism); + + if (DBUS_AUTH_IS_CLIENT (auth)) + (* auth->mech->client_shutdown_func) (auth); + else + (* auth->mech->server_shutdown_func) (auth); + + auth->mech = NULL; + } +} + +/* + * DBUS_COOKIE_SHA1 mechanism + */ + +/* Returns TRUE but with an empty string hash if the + * cookie_id isn't known. As with all this code + * TRUE just means we had enough memory. + */ +static dbus_bool_t +sha1_compute_hash (DBusAuth *auth, + int cookie_id, + const DBusString *server_challenge, + const DBusString *client_challenge, + DBusString *hash) +{ + DBusString cookie; + DBusString to_hash; + dbus_bool_t retval; + + _dbus_assert (auth->keyring != NULL); + + retval = FALSE; + + if (!_dbus_string_init (&cookie)) + return FALSE; + + if (!_dbus_keyring_get_hex_key (auth->keyring, cookie_id, + &cookie)) + goto out_0; + + if (_dbus_string_get_length (&cookie) == 0) + { + retval = TRUE; + goto out_0; + } + + if (!_dbus_string_init (&to_hash)) + goto out_0; + + if (!_dbus_string_copy (server_challenge, 0, + &to_hash, _dbus_string_get_length (&to_hash))) + goto out_1; + + if (!_dbus_string_append (&to_hash, ":")) + goto out_1; + + if (!_dbus_string_copy (client_challenge, 0, + &to_hash, _dbus_string_get_length (&to_hash))) + goto out_1; + + if (!_dbus_string_append (&to_hash, ":")) + goto out_1; + + if (!_dbus_string_copy (&cookie, 0, + &to_hash, _dbus_string_get_length (&to_hash))) + goto out_1; + + if (!_dbus_sha_compute (&to_hash, hash)) + goto out_1; + + retval = TRUE; + + out_1: + _dbus_string_zero (&to_hash); + _dbus_string_free (&to_hash); + out_0: + _dbus_string_zero (&cookie); + _dbus_string_free (&cookie); + return retval; +} + +/** http://www.ietf.org/rfc/rfc2831.txt suggests at least 64 bits of + * entropy, we use 128. This is the number of bytes in the random + * challenge. + */ +#define N_CHALLENGE_BYTES (128/8) + +static dbus_bool_t +sha1_handle_first_client_response (DBusAuth *auth, + const DBusString *data) +{ + /* We haven't sent a challenge yet, we're expecting a desired + * username from the client. + */ + DBusString tmp; + DBusString tmp2; + dbus_bool_t retval; + DBusError error; + + retval = FALSE; + + _dbus_string_set_length (&auth->challenge, 0); + + if (_dbus_string_get_length (data) > 0) + { + if (_dbus_string_get_length (&auth->identity) > 0) + { + /* Tried to send two auth identities, wtf */ + _dbus_verbose ("%s: client tried to send auth identity, but we already have one\n", + DBUS_AUTH_NAME (auth)); + return send_rejected (auth); + } + else + { + /* this is our auth identity */ + if (!_dbus_string_copy (data, 0, &auth->identity, 0)) + return FALSE; + } + } + + if (!_dbus_credentials_add_from_user (auth->desired_identity, data)) + { + _dbus_verbose ("%s: Did not get a valid username from client\n", + DBUS_AUTH_NAME (auth)); + return send_rejected (auth); + } + + if (!_dbus_string_init (&tmp)) + return FALSE; + + if (!_dbus_string_init (&tmp2)) + { + _dbus_string_free (&tmp); + return FALSE; + } + + /* we cache the keyring for speed, so here we drop it if it's the + * wrong one. FIXME caching the keyring here is useless since we use + * a different DBusAuth for every connection. + */ + if (auth->keyring && + !_dbus_keyring_is_for_credentials (auth->keyring, + auth->desired_identity)) + { + _dbus_keyring_unref (auth->keyring); + auth->keyring = NULL; + } + + if (auth->keyring == NULL) + { + dbus_error_init (&error); + auth->keyring = _dbus_keyring_new_for_credentials (auth->desired_identity, + &auth->context, + &error); + + if (auth->keyring == NULL) + { + if (dbus_error_has_name (&error, + DBUS_ERROR_NO_MEMORY)) + { + dbus_error_free (&error); + goto out; + } + else + { + _DBUS_ASSERT_ERROR_IS_SET (&error); + _dbus_verbose ("%s: Error loading keyring: %s\n", + DBUS_AUTH_NAME (auth), error.message); + if (send_rejected (auth)) + retval = TRUE; /* retval is only about mem */ + dbus_error_free (&error); + goto out; + } + } + else + { + _dbus_assert (!dbus_error_is_set (&error)); + } + } + + _dbus_assert (auth->keyring != NULL); + + dbus_error_init (&error); + auth->cookie_id = _dbus_keyring_get_best_key (auth->keyring, &error); + if (auth->cookie_id < 0) + { + _DBUS_ASSERT_ERROR_IS_SET (&error); + _dbus_verbose ("%s: Could not get a cookie ID to send to client: %s\n", + DBUS_AUTH_NAME (auth), error.message); + if (send_rejected (auth)) + retval = TRUE; + dbus_error_free (&error); + goto out; + } + else + { + _dbus_assert (!dbus_error_is_set (&error)); + } + + if (!_dbus_string_copy (&auth->context, 0, + &tmp2, _dbus_string_get_length (&tmp2))) + goto out; + + if (!_dbus_string_append (&tmp2, " ")) + goto out; + + if (!_dbus_string_append_int (&tmp2, auth->cookie_id)) + goto out; + + if (!_dbus_string_append (&tmp2, " ")) + goto out; + + if (!_dbus_generate_random_bytes (&tmp, N_CHALLENGE_BYTES)) + goto out; + + _dbus_string_set_length (&auth->challenge, 0); + if (!_dbus_string_hex_encode (&tmp, 0, &auth->challenge, 0)) + goto out; + + if (!_dbus_string_hex_encode (&tmp, 0, &tmp2, + _dbus_string_get_length (&tmp2))) + goto out; + + if (!send_data (auth, &tmp2)) + goto out; + + goto_state (auth, &server_state_waiting_for_data); + retval = TRUE; + + out: + _dbus_string_zero (&tmp); + _dbus_string_free (&tmp); + _dbus_string_zero (&tmp2); + _dbus_string_free (&tmp2); + + return retval; +} + +static dbus_bool_t +sha1_handle_second_client_response (DBusAuth *auth, + const DBusString *data) +{ + /* We are expecting a response which is the hex-encoded client + * challenge, space, then SHA-1 hash of the concatenation of our + * challenge, ":", client challenge, ":", secret key, all + * hex-encoded. + */ + int i; + DBusString client_challenge; + DBusString client_hash; + dbus_bool_t retval; + DBusString correct_hash; + + retval = FALSE; + + if (!_dbus_string_find_blank (data, 0, &i)) + { + _dbus_verbose ("%s: no space separator in client response\n", + DBUS_AUTH_NAME (auth)); + return send_rejected (auth); + } + + if (!_dbus_string_init (&client_challenge)) + goto out_0; + + if (!_dbus_string_init (&client_hash)) + goto out_1; + + if (!_dbus_string_copy_len (data, 0, i, &client_challenge, + 0)) + goto out_2; + + _dbus_string_skip_blank (data, i, &i); + + if (!_dbus_string_copy_len (data, i, + _dbus_string_get_length (data) - i, + &client_hash, + 0)) + goto out_2; + + if (_dbus_string_get_length (&client_challenge) == 0 || + _dbus_string_get_length (&client_hash) == 0) + { + _dbus_verbose ("%s: zero-length client challenge or hash\n", + DBUS_AUTH_NAME (auth)); + if (send_rejected (auth)) + retval = TRUE; + goto out_2; + } + + if (!_dbus_string_init (&correct_hash)) + goto out_2; + + if (!sha1_compute_hash (auth, auth->cookie_id, + &auth->challenge, + &client_challenge, + &correct_hash)) + goto out_3; + + /* if cookie_id was invalid, then we get an empty hash */ + if (_dbus_string_get_length (&correct_hash) == 0) + { + if (send_rejected (auth)) + retval = TRUE; + goto out_3; + } + + if (!_dbus_string_equal (&client_hash, &correct_hash)) + { + if (send_rejected (auth)) + retval = TRUE; + goto out_3; + } + + if (!_dbus_credentials_add_credentials (auth->authorized_identity, + auth->desired_identity)) + goto out_3; + + /* Copy process ID from the socket credentials if it's there + */ + if (!_dbus_credentials_add_credential (auth->authorized_identity, + DBUS_CREDENTIAL_UNIX_PROCESS_ID, + auth->credentials)) + goto out_3; + + if (!send_ok (auth)) + goto out_3; + + _dbus_verbose ("%s: authenticated client using DBUS_COOKIE_SHA1\n", + DBUS_AUTH_NAME (auth)); + + retval = TRUE; + + out_3: + _dbus_string_zero (&correct_hash); + _dbus_string_free (&correct_hash); + out_2: + _dbus_string_zero (&client_hash); + _dbus_string_free (&client_hash); + out_1: + _dbus_string_free (&client_challenge); + out_0: + return retval; +} + +static dbus_bool_t +handle_server_data_cookie_sha1_mech (DBusAuth *auth, + const DBusString *data) +{ + if (auth->cookie_id < 0) + return sha1_handle_first_client_response (auth, data); + else + return sha1_handle_second_client_response (auth, data); +} + +static void +handle_server_shutdown_cookie_sha1_mech (DBusAuth *auth) +{ + auth->cookie_id = -1; + _dbus_string_set_length (&auth->challenge, 0); +} + +static dbus_bool_t +handle_client_initial_response_cookie_sha1_mech (DBusAuth *auth, + DBusString *response) +{ + DBusString username; + dbus_bool_t retval; + + retval = FALSE; + + if (!_dbus_string_init (&username)) + return FALSE; + + if (!_dbus_append_user_from_current_process (&username)) + goto out_0; + + if (!_dbus_string_hex_encode (&username, 0, + response, + _dbus_string_get_length (response))) + goto out_0; + + retval = TRUE; + + out_0: + _dbus_string_free (&username); + + return retval; +} + +static dbus_bool_t +handle_client_data_cookie_sha1_mech (DBusAuth *auth, + const DBusString *data) +{ + /* The data we get from the server should be the cookie context + * name, the cookie ID, and the server challenge, separated by + * spaces. We send back our challenge string and the correct hash. + */ + dbus_bool_t retval; + DBusString context; + DBusString cookie_id_str; + DBusString server_challenge; + DBusString client_challenge; + DBusString correct_hash; + DBusString tmp; + int i, j; + long val; + + retval = FALSE; + + if (!_dbus_string_find_blank (data, 0, &i)) + { + if (send_error (auth, + "Server did not send context/ID/challenge properly")) + retval = TRUE; + goto out_0; + } + + if (!_dbus_string_init (&context)) + goto out_0; + + if (!_dbus_string_copy_len (data, 0, i, + &context, 0)) + goto out_1; + + _dbus_string_skip_blank (data, i, &i); + if (!_dbus_string_find_blank (data, i, &j)) + { + if (send_error (auth, + "Server did not send context/ID/challenge properly")) + retval = TRUE; + goto out_1; + } + + if (!_dbus_string_init (&cookie_id_str)) + goto out_1; + + if (!_dbus_string_copy_len (data, i, j - i, + &cookie_id_str, 0)) + goto out_2; + + if (!_dbus_string_init (&server_challenge)) + goto out_2; + + i = j; + _dbus_string_skip_blank (data, i, &i); + j = _dbus_string_get_length (data); + + if (!_dbus_string_copy_len (data, i, j - i, + &server_challenge, 0)) + goto out_3; + + if (!_dbus_keyring_validate_context (&context)) + { + if (send_error (auth, "Server sent invalid cookie context")) + retval = TRUE; + goto out_3; + } + + if (!_dbus_string_parse_int (&cookie_id_str, 0, &val, NULL)) + { + if (send_error (auth, "Could not parse cookie ID as an integer")) + retval = TRUE; + goto out_3; + } + + if (_dbus_string_get_length (&server_challenge) == 0) + { + if (send_error (auth, "Empty server challenge string")) + retval = TRUE; + goto out_3; + } + + if (auth->keyring == NULL) + { + DBusError error; + + dbus_error_init (&error); + auth->keyring = _dbus_keyring_new_for_credentials (NULL, + &context, + &error); + + if (auth->keyring == NULL) + { + if (dbus_error_has_name (&error, + DBUS_ERROR_NO_MEMORY)) + { + dbus_error_free (&error); + goto out_3; + } + else + { + _DBUS_ASSERT_ERROR_IS_SET (&error); + + _dbus_verbose ("%s: Error loading keyring: %s\n", + DBUS_AUTH_NAME (auth), error.message); + + if (send_error (auth, "Could not load cookie file")) + retval = TRUE; /* retval is only about mem */ + + dbus_error_free (&error); + goto out_3; + } + } + else + { + _dbus_assert (!dbus_error_is_set (&error)); + } + } + + _dbus_assert (auth->keyring != NULL); + + if (!_dbus_string_init (&tmp)) + goto out_3; + + if (!_dbus_generate_random_bytes (&tmp, N_CHALLENGE_BYTES)) + goto out_4; + + if (!_dbus_string_init (&client_challenge)) + goto out_4; + + if (!_dbus_string_hex_encode (&tmp, 0, &client_challenge, 0)) + goto out_5; + + if (!_dbus_string_init (&correct_hash)) + goto out_5; + + if (!sha1_compute_hash (auth, val, + &server_challenge, + &client_challenge, + &correct_hash)) + goto out_6; + + if (_dbus_string_get_length (&correct_hash) == 0) + { + /* couldn't find the cookie ID or something */ + if (send_error (auth, "Don't have the requested cookie ID")) + retval = TRUE; + goto out_6; + } + + _dbus_string_set_length (&tmp, 0); + + if (!_dbus_string_copy (&client_challenge, 0, &tmp, + _dbus_string_get_length (&tmp))) + goto out_6; + + if (!_dbus_string_append (&tmp, " ")) + goto out_6; + + if (!_dbus_string_copy (&correct_hash, 0, &tmp, + _dbus_string_get_length (&tmp))) + goto out_6; + + if (!send_data (auth, &tmp)) + goto out_6; + + retval = TRUE; + + out_6: + _dbus_string_zero (&correct_hash); + _dbus_string_free (&correct_hash); + out_5: + _dbus_string_free (&client_challenge); + out_4: + _dbus_string_zero (&tmp); + _dbus_string_free (&tmp); + out_3: + _dbus_string_free (&server_challenge); + out_2: + _dbus_string_free (&cookie_id_str); + out_1: + _dbus_string_free (&context); + out_0: + return retval; +} + +static void +handle_client_shutdown_cookie_sha1_mech (DBusAuth *auth) +{ + auth->cookie_id = -1; + _dbus_string_set_length (&auth->challenge, 0); +} + +/* + * EXTERNAL mechanism + */ + +static dbus_bool_t +handle_server_data_external_mech (DBusAuth *auth, + const DBusString *data) +{ + if (_dbus_credentials_are_anonymous (auth->credentials)) + { + _dbus_verbose ("%s: no credentials, mechanism EXTERNAL can't authenticate\n", + DBUS_AUTH_NAME (auth)); + return send_rejected (auth); + } + + if (_dbus_string_get_length (data) > 0) + { + if (_dbus_string_get_length (&auth->identity) > 0) + { + /* Tried to send two auth identities, wtf */ + _dbus_verbose ("%s: client tried to send auth identity, but we already have one\n", + DBUS_AUTH_NAME (auth)); + return send_rejected (auth); + } + else + { + /* this is our auth identity */ + if (!_dbus_string_copy (data, 0, &auth->identity, 0)) + return FALSE; + } + } + + /* Poke client for an auth identity, if none given */ + if (_dbus_string_get_length (&auth->identity) == 0 && + !auth->already_asked_for_initial_response) + { + if (send_data (auth, NULL)) + { + _dbus_verbose ("%s: sending empty challenge asking client for auth identity\n", + DBUS_AUTH_NAME (auth)); + auth->already_asked_for_initial_response = TRUE; + goto_state (auth, &server_state_waiting_for_data); + return TRUE; + } + else + return FALSE; + } + + _dbus_credentials_clear (auth->desired_identity); + + /* If auth->identity is still empty here, then client + * responded with an empty string after we poked it for + * an initial response. This means to try to auth the + * identity provided in the credentials. + */ + if (_dbus_string_get_length (&auth->identity) == 0) + { + if (!_dbus_credentials_add_credentials (auth->desired_identity, + auth->credentials)) + { + return FALSE; /* OOM */ + } + } + else + { + if (!_dbus_credentials_add_from_user (auth->desired_identity, + &auth->identity)) + { + _dbus_verbose ("%s: could not get credentials from uid string\n", + DBUS_AUTH_NAME (auth)); + return send_rejected (auth); + } + } + + if (_dbus_credentials_are_anonymous (auth->desired_identity)) + { + _dbus_verbose ("%s: desired user %s is no good\n", + DBUS_AUTH_NAME (auth), + _dbus_string_get_const_data (&auth->identity)); + return send_rejected (auth); + } + + if (_dbus_credentials_are_superset (auth->credentials, + auth->desired_identity)) + { + /* client has authenticated */ + if (!_dbus_credentials_add_credentials (auth->authorized_identity, + auth->desired_identity)) + return FALSE; + + /* also copy process ID from the socket credentials + */ + if (!_dbus_credentials_add_credential (auth->authorized_identity, + DBUS_CREDENTIAL_UNIX_PROCESS_ID, + auth->credentials)) + return FALSE; + + /* also copy audit data from the socket credentials + */ + if (!_dbus_credentials_add_credential (auth->authorized_identity, + DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID, + auth->credentials)) + return FALSE; + + if (!send_ok (auth)) + return FALSE; + + _dbus_verbose ("%s: authenticated client based on socket credentials\n", + DBUS_AUTH_NAME (auth)); + + return TRUE; + } + else + { + _dbus_verbose ("%s: desired identity not found in socket credentials\n", + DBUS_AUTH_NAME (auth)); + return send_rejected (auth); + } +} + +static void +handle_server_shutdown_external_mech (DBusAuth *auth) +{ + +} + +static dbus_bool_t +handle_client_initial_response_external_mech (DBusAuth *auth, + DBusString *response) +{ + /* We always append our UID as an initial response, so the server + * doesn't have to send back an empty challenge to check whether we + * want to specify an identity. i.e. this avoids a round trip that + * the spec for the EXTERNAL mechanism otherwise requires. + */ + DBusString plaintext; + + if (!_dbus_string_init (&plaintext)) + return FALSE; + + if (!_dbus_append_user_from_current_process (&plaintext)) + goto failed; + + if (!_dbus_string_hex_encode (&plaintext, 0, + response, + _dbus_string_get_length (response))) + goto failed; + + _dbus_string_free (&plaintext); + + return TRUE; + + failed: + _dbus_string_free (&plaintext); + return FALSE; +} + +static dbus_bool_t +handle_client_data_external_mech (DBusAuth *auth, + const DBusString *data) +{ + + return TRUE; +} + +static void +handle_client_shutdown_external_mech (DBusAuth *auth) +{ + +} + +/* + * ANONYMOUS mechanism + */ + +static dbus_bool_t +handle_server_data_anonymous_mech (DBusAuth *auth, + const DBusString *data) +{ + if (_dbus_string_get_length (data) > 0) + { + /* Client is allowed to send "trace" data, the only defined + * meaning is that if it contains '@' it is an email address, + * and otherwise it is anything else, and it's supposed to be + * UTF-8 + */ + if (!_dbus_string_validate_utf8 (data, 0, _dbus_string_get_length (data))) + { + _dbus_verbose ("%s: Received invalid UTF-8 trace data from ANONYMOUS client\n", + DBUS_AUTH_NAME (auth)); + + { + DBusString plaintext; + DBusString encoded; + _dbus_string_init_const (&plaintext, "D-Bus " VERSION); + _dbus_string_init (&encoded); + _dbus_string_hex_encode (&plaintext, 0, + &encoded, + 0); + _dbus_verbose ("%s: try '%s'\n", + DBUS_AUTH_NAME (auth), _dbus_string_get_const_data (&encoded)); + } + return send_rejected (auth); + } + + _dbus_verbose ("%s: ANONYMOUS client sent trace string: '%s'\n", + DBUS_AUTH_NAME (auth), + _dbus_string_get_const_data (data)); + } + + /* We want to be anonymous (clear in case some other protocol got midway through I guess) */ + _dbus_credentials_clear (auth->desired_identity); + + /* Copy process ID from the socket credentials + */ + if (!_dbus_credentials_add_credential (auth->authorized_identity, + DBUS_CREDENTIAL_UNIX_PROCESS_ID, + auth->credentials)) + return FALSE; + + /* Anonymous is always allowed */ + if (!send_ok (auth)) + return FALSE; + + _dbus_verbose ("%s: authenticated client as anonymous\n", + DBUS_AUTH_NAME (auth)); + + return TRUE; +} + +static void +handle_server_shutdown_anonymous_mech (DBusAuth *auth) +{ + +} + +static dbus_bool_t +handle_client_initial_response_anonymous_mech (DBusAuth *auth, + DBusString *response) +{ + /* Our initial response is a "trace" string which must be valid UTF-8 + * and must be an email address if it contains '@'. + * We just send the dbus implementation info, like a user-agent or + * something, because... why not. There's nothing guaranteed here + * though, we could change it later. + */ + DBusString plaintext; + + if (!_dbus_string_init (&plaintext)) + return FALSE; + + if (!_dbus_string_append (&plaintext, + "libdbus " VERSION)) + goto failed; + + if (!_dbus_string_hex_encode (&plaintext, 0, + response, + _dbus_string_get_length (response))) + goto failed; + + _dbus_string_free (&plaintext); + + return TRUE; + + failed: + _dbus_string_free (&plaintext); + return FALSE; +} + +static dbus_bool_t +handle_client_data_anonymous_mech (DBusAuth *auth, + const DBusString *data) +{ + + return TRUE; +} + +static void +handle_client_shutdown_anonymous_mech (DBusAuth *auth) +{ + +} + +/* Put mechanisms here in order of preference. + * Right now we have: + * + * - EXTERNAL checks socket credentials (or in the future, other info from the OS) + * - DBUS_COOKIE_SHA1 uses a cookie in the home directory, like xauth or ICE + * - ANONYMOUS checks nothing but doesn't auth the person as a user + * + * We might ideally add a mechanism to chain to Cyrus SASL so we can + * use its mechanisms as well. + * + */ +static const DBusAuthMechanismHandler +all_mechanisms[] = { + { "EXTERNAL", + handle_server_data_external_mech, + NULL, NULL, + handle_server_shutdown_external_mech, + handle_client_initial_response_external_mech, + handle_client_data_external_mech, + NULL, NULL, + handle_client_shutdown_external_mech }, + { "DBUS_COOKIE_SHA1", + handle_server_data_cookie_sha1_mech, + NULL, NULL, + handle_server_shutdown_cookie_sha1_mech, + handle_client_initial_response_cookie_sha1_mech, + handle_client_data_cookie_sha1_mech, + NULL, NULL, + handle_client_shutdown_cookie_sha1_mech }, + { "ANONYMOUS", + handle_server_data_anonymous_mech, + NULL, NULL, + handle_server_shutdown_anonymous_mech, + handle_client_initial_response_anonymous_mech, + handle_client_data_anonymous_mech, + NULL, NULL, + handle_client_shutdown_anonymous_mech }, + { NULL, NULL } +}; + +static const DBusAuthMechanismHandler* +find_mech (const DBusString *name, + char **allowed_mechs) +{ + int i; + + if (allowed_mechs != NULL && + !_dbus_string_array_contains ((const char**) allowed_mechs, + _dbus_string_get_const_data (name))) + return NULL; + + i = 0; + while (all_mechanisms[i].mechanism != NULL) + { + if (_dbus_string_equal_c_str (name, + all_mechanisms[i].mechanism)) + + return &all_mechanisms[i]; + + ++i; + } + + return NULL; +} + +static dbus_bool_t +send_auth (DBusAuth *auth, const DBusAuthMechanismHandler *mech) +{ + DBusString auth_command; + + if (!_dbus_string_init (&auth_command)) + return FALSE; + + if (!_dbus_string_append (&auth_command, + "AUTH ")) + { + _dbus_string_free (&auth_command); + return FALSE; + } + + if (!_dbus_string_append (&auth_command, + mech->mechanism)) + { + _dbus_string_free (&auth_command); + return FALSE; + } + + if (mech->client_initial_response_func != NULL) + { + if (!_dbus_string_append (&auth_command, " ")) + { + _dbus_string_free (&auth_command); + return FALSE; + } + + if (!(* mech->client_initial_response_func) (auth, &auth_command)) + { + _dbus_string_free (&auth_command); + return FALSE; + } + } + + if (!_dbus_string_append (&auth_command, + "\r\n")) + { + _dbus_string_free (&auth_command); + return FALSE; + } + + if (!_dbus_string_copy (&auth_command, 0, + &auth->outgoing, + _dbus_string_get_length (&auth->outgoing))) + { + _dbus_string_free (&auth_command); + return FALSE; + } + + _dbus_string_free (&auth_command); + shutdown_mech (auth); + auth->mech = mech; + goto_state (auth, &client_state_waiting_for_data); + + return TRUE; +} + +static dbus_bool_t +send_data (DBusAuth *auth, DBusString *data) +{ + int old_len; + + if (data == NULL || _dbus_string_get_length (data) == 0) + return _dbus_string_append (&auth->outgoing, "DATA\r\n"); + else + { + old_len = _dbus_string_get_length (&auth->outgoing); + if (!_dbus_string_append (&auth->outgoing, "DATA ")) + goto out; + + if (!_dbus_string_hex_encode (data, 0, &auth->outgoing, + _dbus_string_get_length (&auth->outgoing))) + goto out; + + if (!_dbus_string_append (&auth->outgoing, "\r\n")) + goto out; + + return TRUE; + + out: + _dbus_string_set_length (&auth->outgoing, old_len); + + return FALSE; + } +} + +static dbus_bool_t +send_rejected (DBusAuth *auth) +{ + DBusString command; + DBusAuthServer *server_auth; + int i; + + if (!_dbus_string_init (&command)) + return FALSE; + + if (!_dbus_string_append (&command, + "REJECTED")) + goto nomem; + + i = 0; + while (all_mechanisms[i].mechanism != NULL) + { + if (!_dbus_string_append (&command, + " ")) + goto nomem; + + if (!_dbus_string_append (&command, + all_mechanisms[i].mechanism)) + goto nomem; + + ++i; + } + + if (!_dbus_string_append (&command, "\r\n")) + goto nomem; + + if (!_dbus_string_copy (&command, 0, &auth->outgoing, + _dbus_string_get_length (&auth->outgoing))) + goto nomem; + + shutdown_mech (auth); + + _dbus_assert (DBUS_AUTH_IS_SERVER (auth)); + server_auth = DBUS_AUTH_SERVER (auth); + server_auth->failures += 1; + + if (server_auth->failures >= server_auth->max_failures) + goto_state (auth, &common_state_need_disconnect); + else + goto_state (auth, &server_state_waiting_for_auth); + + _dbus_string_free (&command); + + return TRUE; + + nomem: + _dbus_string_free (&command); + return FALSE; +} + +static dbus_bool_t +send_error (DBusAuth *auth, const char *message) +{ + return _dbus_string_append_printf (&auth->outgoing, + "ERROR \"%s\"\r\n", message); +} + +static dbus_bool_t +send_ok (DBusAuth *auth) +{ + int orig_len; + + orig_len = _dbus_string_get_length (&auth->outgoing); + + if (_dbus_string_append (&auth->outgoing, "OK ") && + _dbus_string_copy (& DBUS_AUTH_SERVER (auth)->guid, + 0, + &auth->outgoing, + _dbus_string_get_length (&auth->outgoing)) && + _dbus_string_append (&auth->outgoing, "\r\n")) + { + goto_state (auth, &server_state_waiting_for_begin); + return TRUE; + } + else + { + _dbus_string_set_length (&auth->outgoing, orig_len); + return FALSE; + } +} + +static dbus_bool_t +send_begin (DBusAuth *auth, + const DBusString *args_from_ok) +{ + int end_of_hex; + + /* "args_from_ok" should be the GUID, whitespace already pulled off the front */ + _dbus_assert (_dbus_string_get_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server) == 0); + + /* We decode the hex string to binary, using guid_from_server as scratch... */ + + end_of_hex = 0; + if (!_dbus_string_hex_decode (args_from_ok, 0, &end_of_hex, + & DBUS_AUTH_CLIENT (auth)->guid_from_server, 0)) + return FALSE; + + /* now clear out the scratch */ + _dbus_string_set_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server, 0); + + if (end_of_hex != _dbus_string_get_length (args_from_ok) || + end_of_hex == 0) + { + _dbus_verbose ("Bad GUID from server, parsed %d bytes and had %d bytes from server\n", + end_of_hex, _dbus_string_get_length (args_from_ok)); + goto_state (auth, &common_state_need_disconnect); + return TRUE; + } + + if (_dbus_string_copy (args_from_ok, 0, &DBUS_AUTH_CLIENT (auth)->guid_from_server, 0) && + _dbus_string_append (&auth->outgoing, "BEGIN\r\n")) + { + _dbus_verbose ("Got GUID '%s' from the server\n", + _dbus_string_get_const_data (& DBUS_AUTH_CLIENT (auth)->guid_from_server)); + + goto_state (auth, &common_state_authenticated); + return TRUE; + } + else + { + _dbus_string_set_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server, 0); + return FALSE; + } +} + +static dbus_bool_t +send_cancel (DBusAuth *auth) +{ + if (_dbus_string_append (&auth->outgoing, "CANCEL\r\n")) + { + goto_state (auth, &client_state_waiting_for_reject); + return TRUE; + } + else + return FALSE; +} + +static dbus_bool_t +process_data (DBusAuth *auth, + const DBusString *args, + DBusAuthDataFunction data_func) +{ + int end; + DBusString decoded; + + if (!_dbus_string_init (&decoded)) + return FALSE; + + if (!_dbus_string_hex_decode (args, 0, &end, &decoded, 0)) + { + _dbus_string_free (&decoded); + return FALSE; + } + + if (_dbus_string_get_length (args) != end) + { + _dbus_string_free (&decoded); + if (!send_error (auth, "Invalid hex encoding")) + return FALSE; + + return TRUE; + } + +#ifdef DBUS_ENABLE_VERBOSE_MODE + if (_dbus_string_validate_ascii (&decoded, 0, + _dbus_string_get_length (&decoded))) + _dbus_verbose ("%s: data: '%s'\n", + DBUS_AUTH_NAME (auth), + _dbus_string_get_const_data (&decoded)); +#endif + + if (!(* data_func) (auth, &decoded)) + { + _dbus_string_free (&decoded); + return FALSE; + } + + _dbus_string_free (&decoded); + return TRUE; +} + +static dbus_bool_t +handle_auth (DBusAuth *auth, const DBusString *args) +{ + if (_dbus_string_get_length (args) == 0) + { + /* No args to the auth, send mechanisms */ + if (!send_rejected (auth)) + return FALSE; + + return TRUE; + } + else + { + int i; + DBusString mech; + DBusString hex_response; + + _dbus_string_find_blank (args, 0, &i); + + if (!_dbus_string_init (&mech)) + return FALSE; + + if (!_dbus_string_init (&hex_response)) + { + _dbus_string_free (&mech); + return FALSE; + } + + if (!_dbus_string_copy_len (args, 0, i, &mech, 0)) + goto failed; + + _dbus_string_skip_blank (args, i, &i); + if (!_dbus_string_copy (args, i, &hex_response, 0)) + goto failed; + + auth->mech = find_mech (&mech, auth->allowed_mechs); + if (auth->mech != NULL) + { + _dbus_verbose ("%s: Trying mechanism %s\n", + DBUS_AUTH_NAME (auth), + auth->mech->mechanism); + + if (!process_data (auth, &hex_response, + auth->mech->server_data_func)) + goto failed; + } + else + { + /* Unsupported mechanism */ + _dbus_verbose ("%s: Unsupported mechanism %s\n", + DBUS_AUTH_NAME (auth), + _dbus_string_get_const_data (&mech)); + + if (!send_rejected (auth)) + goto failed; + } + + _dbus_string_free (&mech); + _dbus_string_free (&hex_response); + + return TRUE; + + failed: + auth->mech = NULL; + _dbus_string_free (&mech); + _dbus_string_free (&hex_response); + return FALSE; + } +} + +static dbus_bool_t +handle_server_state_waiting_for_auth (DBusAuth *auth, + DBusAuthCommand command, + const DBusString *args) +{ + switch (command) + { + case DBUS_AUTH_COMMAND_AUTH: + return handle_auth (auth, args); + + case DBUS_AUTH_COMMAND_CANCEL: + case DBUS_AUTH_COMMAND_DATA: + return send_error (auth, "Not currently in an auth conversation"); + + case DBUS_AUTH_COMMAND_BEGIN: + goto_state (auth, &common_state_need_disconnect); + return TRUE; + + case DBUS_AUTH_COMMAND_ERROR: + return send_rejected (auth); + + case DBUS_AUTH_COMMAND_REJECTED: + case DBUS_AUTH_COMMAND_OK: + case DBUS_AUTH_COMMAND_UNKNOWN: + default: + return send_error (auth, "Unknown command"); + } +} + +static dbus_bool_t +handle_server_state_waiting_for_data (DBusAuth *auth, + DBusAuthCommand command, + const DBusString *args) +{ + switch (command) + { + case DBUS_AUTH_COMMAND_AUTH: + return send_error (auth, "Sent AUTH while another AUTH in progress"); + + case DBUS_AUTH_COMMAND_CANCEL: + case DBUS_AUTH_COMMAND_ERROR: + return send_rejected (auth); + + case DBUS_AUTH_COMMAND_DATA: + return process_data (auth, args, auth->mech->server_data_func); + + case DBUS_AUTH_COMMAND_BEGIN: + goto_state (auth, &common_state_need_disconnect); + return TRUE; + + case DBUS_AUTH_COMMAND_REJECTED: + case DBUS_AUTH_COMMAND_OK: + case DBUS_AUTH_COMMAND_UNKNOWN: + default: + return send_error (auth, "Unknown command"); + } +} + +static dbus_bool_t +handle_server_state_waiting_for_begin (DBusAuth *auth, + DBusAuthCommand command, + const DBusString *args) +{ + switch (command) + { + case DBUS_AUTH_COMMAND_AUTH: + return send_error (auth, "Sent AUTH while expecting BEGIN"); + + case DBUS_AUTH_COMMAND_DATA: + return send_error (auth, "Sent DATA while expecting BEGIN"); + + case DBUS_AUTH_COMMAND_BEGIN: + goto_state (auth, &common_state_authenticated); + return TRUE; + + case DBUS_AUTH_COMMAND_REJECTED: + case DBUS_AUTH_COMMAND_OK: + case DBUS_AUTH_COMMAND_UNKNOWN: + default: + return send_error (auth, "Unknown command"); + + case DBUS_AUTH_COMMAND_CANCEL: + case DBUS_AUTH_COMMAND_ERROR: + return send_rejected (auth); + } +} + +/* return FALSE if no memory, TRUE if all OK */ +static dbus_bool_t +get_word (const DBusString *str, + int *start, + DBusString *word) +{ + int i; + + _dbus_string_skip_blank (str, *start, start); + _dbus_string_find_blank (str, *start, &i); + + if (i > *start) + { + if (!_dbus_string_copy_len (str, *start, i - *start, word, 0)) + return FALSE; + + *start = i; + } + + return TRUE; +} + +static dbus_bool_t +record_mechanisms (DBusAuth *auth, + const DBusString *args) +{ + int next; + int len; + + if (auth->already_got_mechanisms) + return TRUE; + + len = _dbus_string_get_length (args); + + next = 0; + while (next < len) + { + DBusString m; + const DBusAuthMechanismHandler *mech; + + if (!_dbus_string_init (&m)) + goto nomem; + + if (!get_word (args, &next, &m)) + { + _dbus_string_free (&m); + goto nomem; + } + + mech = find_mech (&m, auth->allowed_mechs); + + if (mech != NULL) + { + /* FIXME right now we try mechanisms in the order + * the server lists them; should we do them in + * some more deterministic order? + * + * Probably in all_mechanisms order, our order of + * preference. Of course when the server is us, + * it lists things in that order anyhow. + */ + + if (mech != &all_mechanisms[0]) + { + _dbus_verbose ("%s: Adding mechanism %s to list we will try\n", + DBUS_AUTH_NAME (auth), mech->mechanism); + + if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try, + (void*) mech)) + { + _dbus_string_free (&m); + goto nomem; + } + } + else + { + _dbus_verbose ("%s: Already tried mechanism %s; not adding to list we will try\n", + DBUS_AUTH_NAME (auth), mech->mechanism); + } + } + else + { + _dbus_verbose ("%s: Server offered mechanism \"%s\" that we don't know how to use\n", + DBUS_AUTH_NAME (auth), + _dbus_string_get_const_data (&m)); + } + + _dbus_string_free (&m); + } + + auth->already_got_mechanisms = TRUE; + + return TRUE; + + nomem: + _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try); + + return FALSE; +} + +static dbus_bool_t +process_rejected (DBusAuth *auth, const DBusString *args) +{ + const DBusAuthMechanismHandler *mech; + DBusAuthClient *client; + + client = DBUS_AUTH_CLIENT (auth); + + if (!auth->already_got_mechanisms) + { + if (!record_mechanisms (auth, args)) + return FALSE; + } + + if (DBUS_AUTH_CLIENT (auth)->mechs_to_try != NULL) + { + mech = client->mechs_to_try->data; + + if (!send_auth (auth, mech)) + return FALSE; + + _dbus_list_pop_first (&client->mechs_to_try); + + _dbus_verbose ("%s: Trying mechanism %s\n", + DBUS_AUTH_NAME (auth), + mech->mechanism); + } + else + { + /* Give up */ + _dbus_verbose ("%s: Disconnecting because we are out of mechanisms to try using\n", + DBUS_AUTH_NAME (auth)); + goto_state (auth, &common_state_need_disconnect); + } + + return TRUE; +} + + +static dbus_bool_t +handle_client_state_waiting_for_data (DBusAuth *auth, + DBusAuthCommand command, + const DBusString *args) +{ + _dbus_assert (auth->mech != NULL); + + switch (command) + { + case DBUS_AUTH_COMMAND_DATA: + return process_data (auth, args, auth->mech->client_data_func); + + case DBUS_AUTH_COMMAND_REJECTED: + return process_rejected (auth, args); + + case DBUS_AUTH_COMMAND_OK: + return send_begin (auth, args); + + case DBUS_AUTH_COMMAND_ERROR: + return send_cancel (auth); + + case DBUS_AUTH_COMMAND_AUTH: + case DBUS_AUTH_COMMAND_CANCEL: + case DBUS_AUTH_COMMAND_BEGIN: + case DBUS_AUTH_COMMAND_UNKNOWN: + default: + return send_error (auth, "Unknown command"); + } +} + +static dbus_bool_t +handle_client_state_waiting_for_ok (DBusAuth *auth, + DBusAuthCommand command, + const DBusString *args) +{ + switch (command) + { + case DBUS_AUTH_COMMAND_REJECTED: + return process_rejected (auth, args); + + case DBUS_AUTH_COMMAND_OK: + return send_begin (auth, args); + + case DBUS_AUTH_COMMAND_DATA: + case DBUS_AUTH_COMMAND_ERROR: + return send_cancel (auth); + + case DBUS_AUTH_COMMAND_AUTH: + case DBUS_AUTH_COMMAND_CANCEL: + case DBUS_AUTH_COMMAND_BEGIN: + case DBUS_AUTH_COMMAND_UNKNOWN: + default: + return send_error (auth, "Unknown command"); + } +} + +static dbus_bool_t +handle_client_state_waiting_for_reject (DBusAuth *auth, + DBusAuthCommand command, + const DBusString *args) +{ + switch (command) + { + case DBUS_AUTH_COMMAND_REJECTED: + return process_rejected (auth, args); + + case DBUS_AUTH_COMMAND_AUTH: + case DBUS_AUTH_COMMAND_CANCEL: + case DBUS_AUTH_COMMAND_DATA: + case DBUS_AUTH_COMMAND_BEGIN: + case DBUS_AUTH_COMMAND_OK: + case DBUS_AUTH_COMMAND_ERROR: + case DBUS_AUTH_COMMAND_UNKNOWN: + default: + goto_state (auth, &common_state_need_disconnect); + return TRUE; + } +} + +/** + * Mapping from command name to enum + */ +typedef struct { + const char *name; /**< Name of the command */ + DBusAuthCommand command; /**< Corresponding enum */ +} DBusAuthCommandName; + +static const DBusAuthCommandName auth_command_names[] = { + { "AUTH", DBUS_AUTH_COMMAND_AUTH }, + { "CANCEL", DBUS_AUTH_COMMAND_CANCEL }, + { "DATA", DBUS_AUTH_COMMAND_DATA }, + { "BEGIN", DBUS_AUTH_COMMAND_BEGIN }, + { "REJECTED", DBUS_AUTH_COMMAND_REJECTED }, + { "OK", DBUS_AUTH_COMMAND_OK }, + { "ERROR", DBUS_AUTH_COMMAND_ERROR } +}; + +static DBusAuthCommand +lookup_command_from_name (DBusString *command) +{ + int i; + + for (i = 0; i < _DBUS_N_ELEMENTS (auth_command_names); i++) + { + if (_dbus_string_equal_c_str (command, + auth_command_names[i].name)) + return auth_command_names[i].command; + } + + return DBUS_AUTH_COMMAND_UNKNOWN; +} + +static void +goto_state (DBusAuth *auth, + const DBusAuthStateData *state) +{ + _dbus_verbose ("%s: going from state %s to state %s\n", + DBUS_AUTH_NAME (auth), + auth->state->name, + state->name); + + auth->state = state; +} + +/* returns whether to call it again right away */ +static dbus_bool_t +process_command (DBusAuth *auth) +{ + DBusAuthCommand command; + DBusString line; + DBusString args; + int eol; + int i, j; + dbus_bool_t retval; + + /* _dbus_verbose ("%s: trying process_command()\n"); */ + + retval = FALSE; + + eol = 0; + if (!_dbus_string_find (&auth->incoming, 0, "\r\n", &eol)) + return FALSE; + + if (!_dbus_string_init (&line)) + { + auth->needed_memory = TRUE; + return FALSE; + } + + if (!_dbus_string_init (&args)) + { + _dbus_string_free (&line); + auth->needed_memory = TRUE; + return FALSE; + } + + if (!_dbus_string_copy_len (&auth->incoming, 0, eol, &line, 0)) + goto out; + + if (!_dbus_string_validate_ascii (&line, 0, + _dbus_string_get_length (&line))) + { + _dbus_verbose ("%s: Command contained non-ASCII chars or embedded nul\n", + DBUS_AUTH_NAME (auth)); + if (!send_error (auth, "Command contained non-ASCII")) + goto out; + else + goto next_command; + } + + _dbus_verbose ("%s: got command \"%s\"\n", + DBUS_AUTH_NAME (auth), + _dbus_string_get_const_data (&line)); + + _dbus_string_find_blank (&line, 0, &i); + _dbus_string_skip_blank (&line, i, &j); + + if (j > i) + _dbus_string_delete (&line, i, j - i); + + if (!_dbus_string_move (&line, i, &args, 0)) + goto out; + + /* FIXME 1.0 we should probably validate that only the allowed + * chars are in the command name + */ + + command = lookup_command_from_name (&line); + if (!(* auth->state->handler) (auth, command, &args)) + goto out; + + next_command: + + /* We've succeeded in processing the whole command so drop it out + * of the incoming buffer and return TRUE to try another command. + */ + + _dbus_string_delete (&auth->incoming, 0, eol); + + /* kill the \r\n */ + _dbus_string_delete (&auth->incoming, 0, 2); + + retval = TRUE; + + out: + _dbus_string_free (&args); + _dbus_string_free (&line); + + if (!retval) + auth->needed_memory = TRUE; + else + auth->needed_memory = FALSE; + + return retval; +} + + +/** @} */ + +/** + * @addtogroup DBusAuth + * @{ + */ + +/** + * Creates a new auth conversation object for the server side. + * See doc/dbus-sasl-profile.txt for full details on what + * this object does. + * + * @returns the new object or #NULL if no memory + */ +DBusAuth* +_dbus_auth_server_new (const DBusString *guid) +{ + DBusAuth *auth; + DBusAuthServer *server_auth; + DBusString guid_copy; + + if (!_dbus_string_init (&guid_copy)) + return NULL; + + if (!_dbus_string_copy (guid, 0, &guid_copy, 0)) + { + _dbus_string_free (&guid_copy); + return NULL; + } + + auth = _dbus_auth_new (sizeof (DBusAuthServer)); + if (auth == NULL) + { + _dbus_string_free (&guid_copy); + return NULL; + } + + auth->side = auth_side_server; + auth->state = &server_state_waiting_for_auth; + + server_auth = DBUS_AUTH_SERVER (auth); + + server_auth->guid = guid_copy; + + /* perhaps this should be per-mechanism with a lower + * max + */ + server_auth->failures = 0; + server_auth->max_failures = 6; + + return auth; +} + +/** + * Creates a new auth conversation object for the client side. + * See doc/dbus-sasl-profile.txt for full details on what + * this object does. + * + * @returns the new object or #NULL if no memory + */ +DBusAuth* +_dbus_auth_client_new (void) +{ + DBusAuth *auth; + DBusString guid_str; + + if (!_dbus_string_init (&guid_str)) + return NULL; + + auth = _dbus_auth_new (sizeof (DBusAuthClient)); + if (auth == NULL) + { + _dbus_string_free (&guid_str); + return NULL; + } + + DBUS_AUTH_CLIENT (auth)->guid_from_server = guid_str; + + auth->side = auth_side_client; + auth->state = &client_state_need_send_auth; + + /* Start the auth conversation by sending AUTH for our default + * mechanism */ + if (!send_auth (auth, &all_mechanisms[0])) + { + _dbus_auth_unref (auth); + return NULL; + } + + return auth; +} + +/** + * Increments the refcount of an auth object. + * + * @param auth the auth conversation + * @returns the auth conversation + */ +DBusAuth * +_dbus_auth_ref (DBusAuth *auth) +{ + _dbus_assert (auth != NULL); + + auth->refcount += 1; + + return auth; +} + +/** + * Decrements the refcount of an auth object. + * + * @param auth the auth conversation + */ +void +_dbus_auth_unref (DBusAuth *auth) +{ + _dbus_assert (auth != NULL); + _dbus_assert (auth->refcount > 0); + + auth->refcount -= 1; + if (auth->refcount == 0) + { + shutdown_mech (auth); + + if (DBUS_AUTH_IS_CLIENT (auth)) + { + _dbus_string_free (& DBUS_AUTH_CLIENT (auth)->guid_from_server); + _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try); + } + else + { + _dbus_assert (DBUS_AUTH_IS_SERVER (auth)); + + _dbus_string_free (& DBUS_AUTH_SERVER (auth)->guid); + } + + if (auth->keyring) + _dbus_keyring_unref (auth->keyring); + + _dbus_string_free (&auth->context); + _dbus_string_free (&auth->challenge); + _dbus_string_free (&auth->identity); + _dbus_string_free (&auth->incoming); + _dbus_string_free (&auth->outgoing); + + dbus_free_string_array (auth->allowed_mechs); + + _dbus_credentials_unref (auth->credentials); + _dbus_credentials_unref (auth->authorized_identity); + _dbus_credentials_unref (auth->desired_identity); + + dbus_free (auth); + } +} + +/** + * Sets an array of authentication mechanism names + * that we are willing to use. + * + * @param auth the auth conversation + * @param mechanisms #NULL-terminated array of mechanism names + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_auth_set_mechanisms (DBusAuth *auth, + const char **mechanisms) +{ + char **copy; + + if (mechanisms != NULL) + { + copy = _dbus_dup_string_array (mechanisms); + if (copy == NULL) + return FALSE; + } + else + copy = NULL; + + dbus_free_string_array (auth->allowed_mechs); + + auth->allowed_mechs = copy; + + return TRUE; +} + +/** + * @param auth the auth conversation object + * @returns #TRUE if we're in a final state + */ +#define DBUS_AUTH_IN_END_STATE(auth) ((auth)->state->handler == NULL) + +/** + * Analyzes buffered input and moves the auth conversation forward, + * returning the new state of the auth conversation. + * + * @param auth the auth conversation + * @returns the new state + */ +DBusAuthState +_dbus_auth_do_work (DBusAuth *auth) +{ + auth->needed_memory = FALSE; + + /* Max amount we'll buffer up before deciding someone's on crack */ +#define MAX_BUFFER (16 * _DBUS_ONE_KILOBYTE) + + do + { + if (DBUS_AUTH_IN_END_STATE (auth)) + break; + + if (_dbus_string_get_length (&auth->incoming) > MAX_BUFFER || + _dbus_string_get_length (&auth->outgoing) > MAX_BUFFER) + { + goto_state (auth, &common_state_need_disconnect); + _dbus_verbose ("%s: Disconnecting due to excessive data buffered in auth phase\n", + DBUS_AUTH_NAME (auth)); + break; + } + } + while (process_command (auth)); + + if (auth->needed_memory) + return DBUS_AUTH_STATE_WAITING_FOR_MEMORY; + else if (_dbus_string_get_length (&auth->outgoing) > 0) + return DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND; + else if (auth->state == &common_state_need_disconnect) + return DBUS_AUTH_STATE_NEED_DISCONNECT; + else if (auth->state == &common_state_authenticated) + return DBUS_AUTH_STATE_AUTHENTICATED; + else return DBUS_AUTH_STATE_WAITING_FOR_INPUT; +} + +/** + * Gets bytes that need to be sent to the peer we're conversing with. + * After writing some bytes, _dbus_auth_bytes_sent() must be called + * to notify the auth object that they were written. + * + * @param auth the auth conversation + * @param str return location for a ref to the buffer to send + * @returns #FALSE if nothing to send + */ +dbus_bool_t +_dbus_auth_get_bytes_to_send (DBusAuth *auth, + const DBusString **str) +{ + _dbus_assert (auth != NULL); + _dbus_assert (str != NULL); + + *str = NULL; + + if (_dbus_string_get_length (&auth->outgoing) == 0) + return FALSE; + + *str = &auth->outgoing; + + return TRUE; +} + +/** + * Notifies the auth conversation object that + * the given number of bytes of the outgoing buffer + * have been written out. + * + * @param auth the auth conversation + * @param bytes_sent number of bytes written out + */ +void +_dbus_auth_bytes_sent (DBusAuth *auth, + int bytes_sent) +{ + _dbus_verbose ("%s: Sent %d bytes of: %s\n", + DBUS_AUTH_NAME (auth), + bytes_sent, + _dbus_string_get_const_data (&auth->outgoing)); + + _dbus_string_delete (&auth->outgoing, + 0, bytes_sent); +} + +/** + * Get a buffer to be used for reading bytes from the peer we're conversing + * with. Bytes should be appended to this buffer. + * + * @param auth the auth conversation + * @param buffer return location for buffer to append bytes to + */ +void +_dbus_auth_get_buffer (DBusAuth *auth, + DBusString **buffer) +{ + _dbus_assert (auth != NULL); + _dbus_assert (!auth->buffer_outstanding); + + *buffer = &auth->incoming; + + auth->buffer_outstanding = TRUE; +} + +/** + * Returns a buffer with new data read into it. + * + * @param auth the auth conversation + * @param buffer the buffer being returned + * @param bytes_read number of new bytes added + */ +void +_dbus_auth_return_buffer (DBusAuth *auth, + DBusString *buffer, + int bytes_read) +{ + _dbus_assert (buffer == &auth->incoming); + _dbus_assert (auth->buffer_outstanding); + + auth->buffer_outstanding = FALSE; +} + +/** + * Returns leftover bytes that were not used as part of the auth + * conversation. These bytes will be part of the message stream + * instead. This function may not be called until authentication has + * succeeded. + * + * @param auth the auth conversation + * @param str return location for pointer to string of unused bytes + */ +void +_dbus_auth_get_unused_bytes (DBusAuth *auth, + const DBusString **str) +{ + if (!DBUS_AUTH_IN_END_STATE (auth)) + return; + + *str = &auth->incoming; +} + + +/** + * Gets rid of unused bytes returned by _dbus_auth_get_unused_bytes() + * after we've gotten them and successfully moved them elsewhere. + * + * @param auth the auth conversation + */ +void +_dbus_auth_delete_unused_bytes (DBusAuth *auth) +{ + if (!DBUS_AUTH_IN_END_STATE (auth)) + return; + + _dbus_string_set_length (&auth->incoming, 0); +} + +/** + * Called post-authentication, indicates whether we need to encode + * the message stream with _dbus_auth_encode_data() prior to + * sending it to the peer. + * + * @param auth the auth conversation + * @returns #TRUE if we need to encode the stream + */ +dbus_bool_t +_dbus_auth_needs_encoding (DBusAuth *auth) +{ + if (auth->state != &common_state_authenticated) + return FALSE; + + if (auth->mech != NULL) + { + if (DBUS_AUTH_IS_CLIENT (auth)) + return auth->mech->client_encode_func != NULL; + else + return auth->mech->server_encode_func != NULL; + } + else + return FALSE; +} + +/** + * Called post-authentication, encodes a block of bytes for sending to + * the peer. If no encoding was negotiated, just copies the bytes + * (you can avoid this by checking _dbus_auth_needs_encoding()). + * + * @param auth the auth conversation + * @param plaintext the plain text data + * @param encoded initialized string to where encoded data is appended + * @returns #TRUE if we had enough memory and successfully encoded + */ +dbus_bool_t +_dbus_auth_encode_data (DBusAuth *auth, + const DBusString *plaintext, + DBusString *encoded) +{ + _dbus_assert (plaintext != encoded); + + if (auth->state != &common_state_authenticated) + return FALSE; + + if (_dbus_auth_needs_encoding (auth)) + { + if (DBUS_AUTH_IS_CLIENT (auth)) + return (* auth->mech->client_encode_func) (auth, plaintext, encoded); + else + return (* auth->mech->server_encode_func) (auth, plaintext, encoded); + } + else + { + return _dbus_string_copy (plaintext, 0, encoded, + _dbus_string_get_length (encoded)); + } +} + +/** + * Called post-authentication, indicates whether we need to decode + * the message stream with _dbus_auth_decode_data() after + * receiving it from the peer. + * + * @param auth the auth conversation + * @returns #TRUE if we need to encode the stream + */ +dbus_bool_t +_dbus_auth_needs_decoding (DBusAuth *auth) +{ + if (auth->state != &common_state_authenticated) + return FALSE; + + if (auth->mech != NULL) + { + if (DBUS_AUTH_IS_CLIENT (auth)) + return auth->mech->client_decode_func != NULL; + else + return auth->mech->server_decode_func != NULL; + } + else + return FALSE; +} + + +/** + * Called post-authentication, decodes a block of bytes received from + * the peer. If no encoding was negotiated, just copies the bytes (you + * can avoid this by checking _dbus_auth_needs_decoding()). + * + * @todo 1.0? We need to be able to distinguish "out of memory" error + * from "the data is hosed" error. + * + * @param auth the auth conversation + * @param encoded the encoded data + * @param plaintext initialized string where decoded data is appended + * @returns #TRUE if we had enough memory and successfully decoded + */ +dbus_bool_t +_dbus_auth_decode_data (DBusAuth *auth, + const DBusString *encoded, + DBusString *plaintext) +{ + _dbus_assert (plaintext != encoded); + + if (auth->state != &common_state_authenticated) + return FALSE; + + if (_dbus_auth_needs_decoding (auth)) + { + if (DBUS_AUTH_IS_CLIENT (auth)) + return (* auth->mech->client_decode_func) (auth, encoded, plaintext); + else + return (* auth->mech->server_decode_func) (auth, encoded, plaintext); + } + else + { + return _dbus_string_copy (encoded, 0, plaintext, + _dbus_string_get_length (plaintext)); + } +} + +/** + * Sets credentials received via reliable means from the operating + * system. + * + * @param auth the auth conversation + * @param credentials the credentials received + * @returns #FALSE on OOM + */ +dbus_bool_t +_dbus_auth_set_credentials (DBusAuth *auth, + DBusCredentials *credentials) +{ + _dbus_credentials_clear (auth->credentials); + return _dbus_credentials_add_credentials (auth->credentials, + credentials); +} + +/** + * Gets the identity we authorized the client as. Apps may have + * different policies as to what identities they allow. + * + * Returned credentials are not a copy and should not be modified + * + * @param auth the auth conversation + * @returns the credentials we've authorized BY REFERENCE do not modify + */ +DBusCredentials* +_dbus_auth_get_identity (DBusAuth *auth) +{ + if (auth->state == &common_state_authenticated) + { + return auth->authorized_identity; + } + else + { + /* FIXME instead of this, keep an empty credential around that + * doesn't require allocation or something + */ + /* return empty credentials */ + _dbus_assert (_dbus_credentials_are_empty (auth->authorized_identity)); + return auth->authorized_identity; + } +} + +/** + * Gets the GUID from the server if we've authenticated; gets + * #NULL otherwise. + * @param auth the auth object + * @returns the GUID in ASCII hex format + */ +const char* +_dbus_auth_get_guid_from_server (DBusAuth *auth) +{ + _dbus_assert (DBUS_AUTH_IS_CLIENT (auth)); + + if (auth->state == &common_state_authenticated) + return _dbus_string_get_const_data (& DBUS_AUTH_CLIENT (auth)->guid_from_server); + else + return NULL; +} + +/** + * Sets the "authentication context" which scopes cookies + * with the DBUS_COOKIE_SHA1 auth mechanism for example. + * + * @param auth the auth conversation + * @param context the context + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_auth_set_context (DBusAuth *auth, + const DBusString *context) +{ + return _dbus_string_replace_len (context, 0, _dbus_string_get_length (context), + &auth->context, 0, _dbus_string_get_length (context)); +} + +/** @} */ + +/* tests in dbus-auth-util.c */ diff --git a/src/dbus/dbus-auth.h b/src/dbus/dbus-auth.h new file mode 100644 index 0000000..14f8320 --- /dev/null +++ b/src/dbus/dbus-auth.h @@ -0,0 +1,81 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-auth.h Authentication + * + * Copyright (C) 2002 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef DBUS_AUTH_H +#define DBUS_AUTH_H + +#include +#include +#include +#include + +DBUS_BEGIN_DECLS + +typedef struct DBusAuth DBusAuth; + +typedef enum +{ + DBUS_AUTH_STATE_WAITING_FOR_INPUT, + DBUS_AUTH_STATE_WAITING_FOR_MEMORY, + DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND, + DBUS_AUTH_STATE_NEED_DISCONNECT, + DBUS_AUTH_STATE_AUTHENTICATED +} DBusAuthState; + +DBusAuth* _dbus_auth_server_new (const DBusString *guid); +DBusAuth* _dbus_auth_client_new (void); +DBusAuth* _dbus_auth_ref (DBusAuth *auth); +void _dbus_auth_unref (DBusAuth *auth); +dbus_bool_t _dbus_auth_set_mechanisms (DBusAuth *auth, + const char **mechanisms); +DBusAuthState _dbus_auth_do_work (DBusAuth *auth); +dbus_bool_t _dbus_auth_get_bytes_to_send (DBusAuth *auth, + const DBusString **str); +void _dbus_auth_bytes_sent (DBusAuth *auth, + int bytes_sent); +void _dbus_auth_get_buffer (DBusAuth *auth, + DBusString **buffer); +void _dbus_auth_return_buffer (DBusAuth *auth, + DBusString *buffer, + int bytes_read); +void _dbus_auth_get_unused_bytes (DBusAuth *auth, + const DBusString **str); +void _dbus_auth_delete_unused_bytes (DBusAuth *auth); +dbus_bool_t _dbus_auth_needs_encoding (DBusAuth *auth); +dbus_bool_t _dbus_auth_encode_data (DBusAuth *auth, + const DBusString *plaintext, + DBusString *encoded); +dbus_bool_t _dbus_auth_needs_decoding (DBusAuth *auth); +dbus_bool_t _dbus_auth_decode_data (DBusAuth *auth, + const DBusString *encoded, + DBusString *plaintext); +dbus_bool_t _dbus_auth_set_credentials (DBusAuth *auth, + DBusCredentials *credentials); +DBusCredentials* _dbus_auth_get_identity (DBusAuth *auth); +dbus_bool_t _dbus_auth_set_context (DBusAuth *auth, + const DBusString *context); +const char* _dbus_auth_get_guid_from_server(DBusAuth *auth); + + +DBUS_END_DECLS + +#endif /* DBUS_AUTH_H */ diff --git a/src/dbus/dbus-bus.c b/src/dbus/dbus-bus.c new file mode 100644 index 0000000..f97cce6 --- /dev/null +++ b/src/dbus/dbus-bus.c @@ -0,0 +1,1537 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-bus.c Convenience functions for communicating with the bus. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-bus.h" +#include "dbus-protocol.h" +#include "dbus-internals.h" +#include "dbus-message.h" +#include "dbus-marshal-validate.h" +#include "dbus-threads-internal.h" +#include "dbus-connection-internal.h" +#include + +/** + * @defgroup DBusBus Message bus APIs + * @ingroup DBus + * @brief Functions for communicating with the message bus + * + * dbus_bus_get() allows all modules and libraries in a given + * process to share the same connection to the bus daemon by storing + * the connection globally. + * + * All other functions in this module are just convenience functions; + * most of them invoke methods on the bus daemon, by sending method + * call messages to #DBUS_SERVICE_DBUS. These convenience functions + * often make blocking method calls. If you don't want to block, + * you can send the method call messages manually in the same way + * you would any other method call message. + * + * This module is the only one in libdbus that's specific to + * communicating with the message bus daemon. The rest of the API can + * also be used for connecting to another application directly. + * + * @todo right now the default address of the system bus is hardcoded, + * so if you change it in the global config file suddenly you have to + * set DBUS_SYSTEM_BUS_ADDRESS env variable. Might be nice if the + * client lib somehow read the config file, or if the bus on startup + * somehow wrote out its address to a well-known spot, but might also + * not be worth it. + */ + +/** + * @defgroup DBusBusInternals Message bus APIs internals + * @ingroup DBusInternals + * @brief Internals of functions for communicating with the message bus + * + * @{ + */ + +/** + * Block of message-bus-related data we attach to each + * #DBusConnection used with these convenience functions. + * + */ +typedef struct +{ + DBusConnection *connection; /**< Connection we're associated with */ + char *unique_name; /**< Unique name of this connection */ + + unsigned int is_well_known : 1; /**< Is one of the well-known connections in our global array */ +} BusData; + +/** The slot we have reserved to store BusData. + */ +static dbus_int32_t bus_data_slot = -1; + +/** Number of bus types */ +#define N_BUS_TYPES 3 + +static DBusConnection *bus_connections[N_BUS_TYPES]; +static char *bus_connection_addresses[N_BUS_TYPES] = { NULL, NULL, NULL }; + +static DBusBusType activation_bus_type = DBUS_BUS_STARTER; + +static dbus_bool_t initialized = FALSE; + +/** + * Lock for globals in this file + */ +_DBUS_DEFINE_GLOBAL_LOCK (bus); + +/** + * Global lock covering all BusData on any connection. The bet is + * that some lock contention is better than more memory + * for a per-connection lock, but it's tough to imagine it mattering + * either way. + */ +_DBUS_DEFINE_GLOBAL_LOCK (bus_datas); + +static void +addresses_shutdown_func (void *data) +{ + int i; + + i = 0; + while (i < N_BUS_TYPES) + { + if (bus_connections[i] != NULL) + _dbus_warn_check_failed ("dbus_shutdown() called but connections were still live. This probably means the application did not drop all its references to bus connections.\n"); + + dbus_free (bus_connection_addresses[i]); + bus_connection_addresses[i] = NULL; + ++i; + } + + activation_bus_type = DBUS_BUS_STARTER; + + initialized = FALSE; +} + +static dbus_bool_t +get_from_env (char **connection_p, + const char *env_var) +{ + const char *s; + + _dbus_assert (*connection_p == NULL); + + s = _dbus_getenv (env_var); + if (s == NULL || *s == '\0') + return TRUE; /* successfully didn't use the env var */ + else + { + *connection_p = _dbus_strdup (s); + return *connection_p != NULL; + } +} + +static dbus_bool_t +init_connections_unlocked (void) +{ + if (!initialized) + { + const char *s; + int i; + + i = 0; + while (i < N_BUS_TYPES) + { + bus_connections[i] = NULL; + ++i; + } + + /* Don't init these twice, we may run this code twice if + * init_connections_unlocked() fails midway through. + * In practice, each block below should contain only one + * "return FALSE" or running through twice may not + * work right. + */ + + if (bus_connection_addresses[DBUS_BUS_SYSTEM] == NULL) + { + _dbus_verbose ("Filling in system bus address...\n"); + + if (!get_from_env (&bus_connection_addresses[DBUS_BUS_SYSTEM], + "DBUS_SYSTEM_BUS_ADDRESS")) + return FALSE; + } + + + if (bus_connection_addresses[DBUS_BUS_SYSTEM] == NULL) + { + /* Use default system bus address if none set in environment */ + bus_connection_addresses[DBUS_BUS_SYSTEM] = + _dbus_strdup (DBUS_SYSTEM_BUS_DEFAULT_ADDRESS); + + if (bus_connection_addresses[DBUS_BUS_SYSTEM] == NULL) + return FALSE; + + _dbus_verbose (" used default system bus \"%s\"\n", + bus_connection_addresses[DBUS_BUS_SYSTEM]); + } + else + _dbus_verbose (" used env var system bus \"%s\"\n", + bus_connection_addresses[DBUS_BUS_SYSTEM]); + + if (bus_connection_addresses[DBUS_BUS_SESSION] == NULL) + { + _dbus_verbose ("Filling in session bus address...\n"); + + if (!get_from_env (&bus_connection_addresses[DBUS_BUS_SESSION], + "DBUS_SESSION_BUS_ADDRESS")) + return FALSE; + + if (bus_connection_addresses[DBUS_BUS_SESSION] == NULL) + bus_connection_addresses[DBUS_BUS_SESSION] = + _dbus_strdup (DBUS_SESSION_BUS_DEFAULT_ADDRESS); + + if (bus_connection_addresses[DBUS_BUS_SESSION] == NULL) + return FALSE; + + _dbus_verbose (" \"%s\"\n", bus_connection_addresses[DBUS_BUS_SESSION] ? + bus_connection_addresses[DBUS_BUS_SESSION] : "none set"); + } + + if (bus_connection_addresses[DBUS_BUS_STARTER] == NULL) + { + _dbus_verbose ("Filling in activation bus address...\n"); + + if (!get_from_env (&bus_connection_addresses[DBUS_BUS_STARTER], + "DBUS_STARTER_ADDRESS")) + return FALSE; + + _dbus_verbose (" \"%s\"\n", bus_connection_addresses[DBUS_BUS_STARTER] ? + bus_connection_addresses[DBUS_BUS_STARTER] : "none set"); + } + + + if (bus_connection_addresses[DBUS_BUS_STARTER] != NULL) + { + s = _dbus_getenv ("DBUS_STARTER_BUS_TYPE"); + + if (s != NULL) + { + _dbus_verbose ("Bus activation type was set to \"%s\"\n", s); + + if (strcmp (s, "system") == 0) + activation_bus_type = DBUS_BUS_SYSTEM; + else if (strcmp (s, "session") == 0) + activation_bus_type = DBUS_BUS_SESSION; + } + } + else + { + /* Default to the session bus instead if available */ + if (bus_connection_addresses[DBUS_BUS_SESSION] != NULL) + { + bus_connection_addresses[DBUS_BUS_STARTER] = + _dbus_strdup (bus_connection_addresses[DBUS_BUS_SESSION]); + if (bus_connection_addresses[DBUS_BUS_STARTER] == NULL) + return FALSE; + } + } + + /* If we return FALSE we have to be sure that restarting + * the above code will work right + */ + + if (!_dbus_setenv ("DBUS_ACTIVATION_ADDRESS", NULL)) + return FALSE; + + if (!_dbus_setenv ("DBUS_ACTIVATION_BUS_TYPE", NULL)) + return FALSE; + + if (!_dbus_register_shutdown_func (addresses_shutdown_func, + NULL)) + return FALSE; + + initialized = TRUE; + } + + return initialized; +} + +static void +bus_data_free (void *data) +{ + BusData *bd = data; + + if (bd->is_well_known) + { + int i; + _DBUS_LOCK (bus); + /* We may be stored in more than one slot */ + /* This should now be impossible - these slots are supposed to + * be cleared on disconnect, so should not need to be cleared on + * finalize + */ + i = 0; + while (i < N_BUS_TYPES) + { + if (bus_connections[i] == bd->connection) + bus_connections[i] = NULL; + + ++i; + } + _DBUS_UNLOCK (bus); + } + + dbus_free (bd->unique_name); + dbus_free (bd); + + dbus_connection_free_data_slot (&bus_data_slot); +} + +static BusData* +ensure_bus_data (DBusConnection *connection) +{ + BusData *bd; + + if (!dbus_connection_allocate_data_slot (&bus_data_slot)) + return NULL; + + bd = dbus_connection_get_data (connection, bus_data_slot); + if (bd == NULL) + { + bd = dbus_new0 (BusData, 1); + if (bd == NULL) + { + dbus_connection_free_data_slot (&bus_data_slot); + return NULL; + } + + bd->connection = connection; + + if (!dbus_connection_set_data (connection, bus_data_slot, bd, + bus_data_free)) + { + dbus_free (bd); + dbus_connection_free_data_slot (&bus_data_slot); + return NULL; + } + + /* Data slot refcount now held by the BusData */ + } + else + { + dbus_connection_free_data_slot (&bus_data_slot); + } + + return bd; +} + +/** + * Internal function that checks to see if this + * is a shared connection owned by the bus and if it is unref it. + * + * @param connection a connection that has been disconnected. + */ +void +_dbus_bus_notify_shared_connection_disconnected_unlocked (DBusConnection *connection) +{ + int i; + + _DBUS_LOCK (bus); + + /* We are expecting to have the connection saved in only one of these + * slots, but someone could in a pathological case set system and session + * bus to the same bus or something. Or set one of them to the starter + * bus without setting the starter bus type in the env variable. + * So we don't break the loop as soon as we find a match. + */ + for (i = 0; i < N_BUS_TYPES; ++i) + { + if (bus_connections[i] == connection) + { + bus_connections[i] = NULL; + } + } + + _DBUS_UNLOCK (bus); +} + +static DBusConnection * +internal_bus_get (DBusBusType type, + dbus_bool_t private, + DBusError *error) +{ + const char *address; + DBusConnection *connection; + BusData *bd; + DBusBusType address_type; + + _dbus_return_val_if_fail (type >= 0 && type < N_BUS_TYPES, NULL); + _dbus_return_val_if_error_is_set (error, NULL); + + _DBUS_LOCK (bus); + + if (!init_connections_unlocked ()) + { + _DBUS_UNLOCK (bus); + _DBUS_SET_OOM (error); + return NULL; + } + + /* We want to use the activation address even if the + * activating bus is the session or system bus, + * per the spec. + */ + address_type = type; + + /* Use the real type of the activation bus for getting its + * connection, but only if the real type's address is available. (If + * the activating bus isn't a well-known bus then + * activation_bus_type == DBUS_BUS_STARTER) + */ + if (type == DBUS_BUS_STARTER && + bus_connection_addresses[activation_bus_type] != NULL) + type = activation_bus_type; + + if (!private && bus_connections[type] != NULL) + { + connection = bus_connections[type]; + dbus_connection_ref (connection); + + _DBUS_UNLOCK (bus); + return connection; + } + + address = bus_connection_addresses[address_type]; + if (address == NULL) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Unable to determine the address of the message bus (try 'man dbus-launch' and 'man dbus-daemon' for help)"); + _DBUS_UNLOCK (bus); + return NULL; + } + + if (private) + connection = dbus_connection_open_private (address, error); + else + connection = dbus_connection_open (address, error); + + if (!connection) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + _DBUS_UNLOCK (bus); + return NULL; + } + + if (!dbus_bus_register (connection, error)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + _dbus_connection_close_possibly_shared (connection); + dbus_connection_unref (connection); + + _DBUS_UNLOCK (bus); + return NULL; + } + + if (!private) + { + /* store a weak ref to the connection (dbus-connection.c is + * supposed to have a strong ref that it drops on disconnect, + * since this is a shared connection) + */ + bus_connections[type] = connection; + } + + /* By default we're bound to the lifecycle of + * the message bus. + */ + dbus_connection_set_exit_on_disconnect (connection, + TRUE); + + _DBUS_LOCK (bus_datas); + bd = ensure_bus_data (connection); + _dbus_assert (bd != NULL); /* it should have been created on + register, so OOM not possible */ + bd->is_well_known = TRUE; + _DBUS_UNLOCK (bus_datas); + + + _DBUS_UNLOCK (bus); + + /* Return a reference to the caller */ + return connection; +} + + +/** @} */ /* end of implementation details docs */ + +/** + * @addtogroup DBusBus + * @{ + */ + +/** + * Connects to a bus daemon and registers the client with it. If a + * connection to the bus already exists, then that connection is + * returned. The caller of this function owns a reference to the bus. + * + * The caller may NOT call dbus_connection_close() on this connection; + * see dbus_connection_open() and dbus_connection_close() for details + * on that. + * + * If this function obtains a new connection object never before + * returned from dbus_bus_get(), it will call + * dbus_connection_set_exit_on_disconnect(), so the application + * will exit if the connection closes. You can undo this + * by calling dbus_connection_set_exit_on_disconnect() yourself + * after you get the connection. + * + * dbus_bus_get() calls dbus_bus_register() for you. + * + * If returning a newly-created connection, this function will block + * until authentication and bus registration are complete. + * + * @param type bus type + * @param error address where an error can be returned. + * @returns a #DBusConnection with new ref + */ +DBusConnection * +dbus_bus_get (DBusBusType type, + DBusError *error) +{ + return internal_bus_get (type, FALSE, error); +} + +/** + * Connects to a bus daemon and registers the client with it as with + * dbus_bus_register(). Unlike dbus_bus_get(), always creates a new + * connection. This connection will not be saved or recycled by + * libdbus. Caller owns a reference to the bus and must either close + * it or know it to be closed prior to releasing this reference. + * + * See dbus_connection_open_private() for more details on when to + * close and unref this connection. + * + * This function calls + * dbus_connection_set_exit_on_disconnect() on the new connection, so the application + * will exit if the connection closes. You can undo this + * by calling dbus_connection_set_exit_on_disconnect() yourself + * after you get the connection. + * + * dbus_bus_get_private() calls dbus_bus_register() for you. + * + * This function will block until authentication and bus registration + * are complete. + * + * @param type bus type + * @param error address where an error can be returned. + * @returns a DBusConnection with new ref + */ +DBusConnection * +dbus_bus_get_private (DBusBusType type, + DBusError *error) +{ + return internal_bus_get (type, TRUE, error); +} + +/** + * Registers a connection with the bus. This must be the first + * thing an application does when connecting to the message bus. + * If registration succeeds, the unique name will be set, + * and can be obtained using dbus_bus_get_unique_name(). + * + * This function will block until registration is complete. + * + * If the connection has already registered with the bus + * (determined by checking whether dbus_bus_get_unique_name() + * returns a non-#NULL value), then this function does nothing. + * + * If you use dbus_bus_get() or dbus_bus_get_private() this + * function will be called for you. + * + * @note Just use dbus_bus_get() or dbus_bus_get_private() instead of + * dbus_bus_register() and save yourself some pain. Using + * dbus_bus_register() manually is only useful if you have your + * own custom message bus not found in #DBusBusType. + * + * If you open a bus connection with dbus_connection_open() or + * dbus_connection_open_private() you will have to dbus_bus_register() + * yourself, or make the appropriate registration method calls + * yourself. If you send the method calls yourself, call + * dbus_bus_set_unique_name() with the unique bus name you get from + * the bus. + * + * For shared connections (created with dbus_connection_open()) in a + * multithreaded application, you can't really make the registration + * calls yourself, because you don't know whether some other thread is + * also registering, and the bus will kick you off if you send two + * registration messages. + * + * If you use dbus_bus_register() however, there is a lock that + * keeps both apps from registering at the same time. + * + * The rule in a multithreaded app, then, is that dbus_bus_register() + * must be used to register, or you need to have your own locks that + * all threads in the app will respect. + * + * In a single-threaded application you can register by hand instead + * of using dbus_bus_register(), as long as you check + * dbus_bus_get_unique_name() to see if a unique name has already been + * stored by another thread before you send the registration messages. + * + * @param connection the connection + * @param error place to store errors + * @returns #TRUE on success + */ +dbus_bool_t +dbus_bus_register (DBusConnection *connection, + DBusError *error) +{ + DBusMessage *message, *reply; + char *name; + BusData *bd; + dbus_bool_t retval; + + _dbus_return_val_if_fail (connection != NULL, FALSE); + _dbus_return_val_if_error_is_set (error, FALSE); + + retval = FALSE; + + _DBUS_LOCK (bus_datas); + + bd = ensure_bus_data (connection); + if (bd == NULL) + { + _DBUS_SET_OOM (error); + _DBUS_UNLOCK (bus_datas); + return FALSE; + } + + if (bd->unique_name != NULL) + { + _dbus_verbose ("Ignoring attempt to register the same DBusConnection %s with the message bus a second time.\n", + bd->unique_name); + _DBUS_UNLOCK (bus_datas); + + /* Success! */ + return TRUE; + } + + message = dbus_message_new_method_call (DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "Hello"); + + if (!message) + { + _DBUS_SET_OOM (error); + + _DBUS_UNLOCK (bus_datas); + return FALSE; + } + + reply = dbus_connection_send_with_reply_and_block (connection, message, -1, error); + + dbus_message_unref (message); + + if (reply == NULL) + goto out; + else if (dbus_set_error_from_message (error, reply)) + goto out; + else if (!dbus_message_get_args (reply, error, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID)) + goto out; + + bd->unique_name = _dbus_strdup (name); + if (bd->unique_name == NULL) + { + _DBUS_SET_OOM (error); + goto out; + } + + retval = TRUE; + + out: + if (reply) + dbus_message_unref (reply); + + if (!retval) + _DBUS_ASSERT_ERROR_IS_SET (error); + + _DBUS_UNLOCK (bus_datas); + + return retval; +} + + +/** + * Sets the unique name of the connection, as assigned by the message + * bus. Can only be used if you registered with the bus manually + * (i.e. if you did not call dbus_bus_register()). Can only be called + * once per connection. After the unique name is set, you can get it + * with dbus_bus_get_unique_name(). + * + * The only reason to use this function is to re-implement the + * equivalent of dbus_bus_register() yourself. One (probably unusual) + * reason to do that might be to do the bus registration call + * asynchronously instead of synchronously. + * + * @note Just use dbus_bus_get() or dbus_bus_get_private(), or worst + * case dbus_bus_register(), instead of messing with this + * function. There's really no point creating pain for yourself by + * doing things manually. + * + * It's hard to use this function safely on shared connections + * (created by dbus_connection_open()) in a multithreaded application, + * because only one registration attempt can be sent to the bus. If + * two threads are both sending the registration message, there is no + * mechanism in libdbus itself to avoid sending it twice. + * + * Thus, you need a way to coordinate which thread sends the + * registration attempt; which also means you know which thread + * will call dbus_bus_set_unique_name(). If you don't know + * about all threads in the app (for example, if some libraries + * you're using might start libdbus-using threads), then you + * need to avoid using this function on shared connections. + * + * @param connection the connection + * @param unique_name the unique name + * @returns #FALSE if not enough memory + */ +dbus_bool_t +dbus_bus_set_unique_name (DBusConnection *connection, + const char *unique_name) +{ + BusData *bd; + dbus_bool_t success; + + _dbus_return_val_if_fail (connection != NULL, FALSE); + _dbus_return_val_if_fail (unique_name != NULL, FALSE); + + _DBUS_LOCK (bus_datas); + + bd = ensure_bus_data (connection); + if (bd == NULL) + return FALSE; + + _dbus_assert (bd->unique_name == NULL); + + bd->unique_name = _dbus_strdup (unique_name); + success = bd->unique_name != NULL; + + _DBUS_UNLOCK (bus_datas); + + return success; +} + +/** + * Gets the unique name of the connection as assigned by the message + * bus. Only possible after the connection has been registered with + * the message bus. All connections returned by dbus_bus_get() or + * dbus_bus_get_private() have been successfully registered. + * + * The name remains valid until the connection is freed, and + * should not be freed by the caller. + * + * Other than dbus_bus_get(), there are two ways to set the unique + * name; one is dbus_bus_register(), the other is + * dbus_bus_set_unique_name(). You are responsible for calling + * dbus_bus_set_unique_name() if you register by hand instead of using + * dbus_bus_register(). + * + * @param connection the connection + * @returns the unique name or #NULL on error + */ +const char* +dbus_bus_get_unique_name (DBusConnection *connection) +{ + BusData *bd; + const char *unique_name; + + _dbus_return_val_if_fail (connection != NULL, NULL); + + _DBUS_LOCK (bus_datas); + + bd = ensure_bus_data (connection); + if (bd == NULL) + return NULL; + + unique_name = bd->unique_name; + + _DBUS_UNLOCK (bus_datas); + + return unique_name; +} + +/** + * Asks the bus to return the UID the named connection authenticated + * as, if any. Only works on UNIX; only works for connections on the + * same machine as the bus. If you are not on the same machine as the + * bus, then calling this is probably a bad idea, since the UID will + * mean little to your application. + * + * For the system message bus you're guaranteed to be on the same + * machine since it only listens on a UNIX domain socket (at least, + * as shipped by default). + * + * This function only works for connections that authenticated as + * a UNIX user, right now that includes all bus connections, but + * it's very possible to have connections with no associated UID. + * So check for errors and do something sensible if they happen. + * + * This function will always return an error on Windows. + * + * @param connection the connection + * @param name a name owned by the connection + * @param error location to store the error + * @returns the unix user id, or ((unsigned)-1) if error is set + */ +unsigned long +dbus_bus_get_unix_user (DBusConnection *connection, + const char *name, + DBusError *error) +{ + DBusMessage *message, *reply; + dbus_uint32_t uid; + + _dbus_return_val_if_fail (connection != NULL, DBUS_UID_UNSET); + _dbus_return_val_if_fail (name != NULL, DBUS_UID_UNSET); + _dbus_return_val_if_fail (_dbus_check_is_valid_bus_name (name), DBUS_UID_UNSET); + _dbus_return_val_if_error_is_set (error, DBUS_UID_UNSET); + + message = dbus_message_new_method_call (DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "GetConnectionUnixUser"); + + if (message == NULL) + { + _DBUS_SET_OOM (error); + return DBUS_UID_UNSET; + } + + if (!dbus_message_append_args (message, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID)) + { + dbus_message_unref (message); + _DBUS_SET_OOM (error); + return DBUS_UID_UNSET; + } + + reply = dbus_connection_send_with_reply_and_block (connection, message, -1, + error); + + dbus_message_unref (message); + + if (reply == NULL) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + return DBUS_UID_UNSET; + } + + if (dbus_set_error_from_message (error, reply)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + dbus_message_unref (reply); + return DBUS_UID_UNSET; + } + + if (!dbus_message_get_args (reply, error, + DBUS_TYPE_UINT32, &uid, + DBUS_TYPE_INVALID)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + dbus_message_unref (reply); + return DBUS_UID_UNSET; + } + + dbus_message_unref (reply); + + return (unsigned long) uid; +} + +/** + * Asks the bus to return its globally unique ID, as described in the + * D-Bus specification. For the session bus, this is useful as a way + * to uniquely identify each user session. For the system bus, + * probably the bus ID is not useful; instead, use the machine ID + * since it's accessible without necessarily connecting to the bus and + * may be persistent beyond a single bus instance (across reboots for + * example). See dbus_get_local_machine_id(). + * + * In addition to an ID for each bus and an ID for each machine, there is + * an ID for each address that the bus is listening on; that can + * be retrieved with dbus_connection_get_server_id(), though it is + * probably not very useful. + * + * @param connection the connection + * @param error location to store the error + * @returns the bus ID or #NULL if error is set + */ +char* +dbus_bus_get_id (DBusConnection *connection, + DBusError *error) +{ + DBusMessage *message, *reply; + char *id; + const char *v_STRING; + + _dbus_return_val_if_fail (connection != NULL, NULL); + _dbus_return_val_if_error_is_set (error, NULL); + + message = dbus_message_new_method_call (DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "GetId"); + + if (message == NULL) + { + _DBUS_SET_OOM (error); + return NULL; + } + + reply = dbus_connection_send_with_reply_and_block (connection, message, -1, + error); + + dbus_message_unref (message); + + if (reply == NULL) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + return NULL; + } + + if (dbus_set_error_from_message (error, reply)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + dbus_message_unref (reply); + return NULL; + } + + v_STRING = NULL; + if (!dbus_message_get_args (reply, error, + DBUS_TYPE_STRING, &v_STRING, + DBUS_TYPE_INVALID)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + dbus_message_unref (reply); + return NULL; + } + + id = _dbus_strdup (v_STRING); /* may be NULL */ + + dbus_message_unref (reply); + + if (id == NULL) + _DBUS_SET_OOM (error); + + /* FIXME it might be nice to cache the ID locally */ + + return id; +} + +/** + * Asks the bus to assign the given name to this connection by invoking + * the RequestName method on the bus. This method is fully documented + * in the D-Bus specification. For quick reference, the flags and + * result codes are discussed here, but the specification is the + * canonical version of this information. + * + * First you should know that for each bus name, the bus stores + * a queue of connections that would like to own it. Only + * one owns it at a time - called the primary owner. If the primary + * owner releases the name or disconnects, then the next owner in the + * queue atomically takes over. + * + * So for example if you have an application org.freedesktop.TextEditor + * and multiple instances of it can be run, you can have all of them + * sitting in the queue. The first one to start up will receive messages + * sent to org.freedesktop.TextEditor, but if that one exits another + * will become the primary owner and receive messages. + * + * The queue means you don't need to manually watch for the current owner to + * disappear and then request the name again. + * + * When requesting a name, you can specify several flags. + * + * #DBUS_NAME_FLAG_ALLOW_REPLACEMENT and #DBUS_NAME_FLAG_DO_NOT_QUEUE + * are properties stored by the bus for this connection with respect to + * each requested bus name. These properties are stored even if the + * connection is queued and does not become the primary owner. + * You can update these flags by calling RequestName again (even if + * you already own the name). + * + * #DBUS_NAME_FLAG_ALLOW_REPLACEMENT means that another requestor of the + * name can take it away from you by specifying #DBUS_NAME_FLAG_REPLACE_EXISTING. + * + * #DBUS_NAME_FLAG_DO_NOT_QUEUE means that if you aren't the primary owner, + * you don't want to be queued up - you only care about being the + * primary owner. + * + * Unlike the other two flags, #DBUS_NAME_FLAG_REPLACE_EXISTING is a property + * of the individual RequestName call, i.e. the bus does not persistently + * associate it with the connection-name pair. If a RequestName call includes + * the #DBUS_NAME_FLAG_REPLACE_EXISTING flag, and the current primary + * owner has #DBUS_NAME_FLAG_ALLOW_REPLACEMENT set, then the current primary + * owner will be kicked off. + * + * If no flags are given, an application will receive the requested + * name only if the name is currently unowned; and it will NOT give + * up the name if another application asks to take it over using + * #DBUS_NAME_FLAG_REPLACE_EXISTING. + * + * This function returns a result code. The possible result codes + * are as follows. + * + * #DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER means that the name had no + * existing owner, and the caller is now the primary owner; or that + * the name had an owner, and the caller specified + * #DBUS_NAME_FLAG_REPLACE_EXISTING, and the current owner + * specified #DBUS_NAME_FLAG_ALLOW_REPLACEMENT. + * + * #DBUS_REQUEST_NAME_REPLY_IN_QUEUE happens only if the caller does NOT + * specify #DBUS_NAME_FLAG_DO_NOT_QUEUE and either the current owner + * did NOT specify #DBUS_NAME_FLAG_ALLOW_REPLACEMENT or the caller did NOT + * specify #DBUS_NAME_FLAG_REPLACE_EXISTING. In this case the caller ends up + * in a queue to own the name after the current owner gives it up. + * + * #DBUS_REQUEST_NAME_REPLY_EXISTS happens if the name has an owner + * already and the caller specifies #DBUS_NAME_FLAG_DO_NOT_QUEUE + * and either the current owner has NOT specified + * #DBUS_NAME_FLAG_ALLOW_REPLACEMENT or the caller did NOT specify + * #DBUS_NAME_FLAG_REPLACE_EXISTING. + * + * #DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER happens if an application + * requests a name it already owns. (Re-requesting a name is useful if + * you want to change the #DBUS_NAME_FLAG_ALLOW_REPLACEMENT or + * #DBUS_NAME_FLAG_DO_NOT_QUEUE settings.) + * + * When a service represents an application, say "text editor," then + * it should specify #DBUS_NAME_FLAG_ALLOW_REPLACEMENT if it wants + * the last editor started to be the user's editor vs. the first one + * started. Then any editor that can be the user's editor should + * specify #DBUS_NAME_FLAG_REPLACE_EXISTING to either take over + * (last-started-wins) or be queued up (first-started-wins) according + * to whether #DBUS_NAME_FLAG_ALLOW_REPLACEMENT was given. + * + * Conventionally, single-instance applications often offer a command + * line option called --replace which means to replace the current + * instance. To implement this, always set + * #DBUS_NAME_FLAG_ALLOW_REPLACEMENT when you request your + * application's bus name. When you lose ownership of your bus name, + * you need to exit. Look for the signal "NameLost" from + * #DBUS_SERVICE_DBUS and #DBUS_INTERFACE_DBUS (the signal's first + * argument is the bus name that was lost). If starting up without + * --replace, do not specify #DBUS_NAME_FLAG_REPLACE_EXISTING, and + * exit if you fail to become the bus name owner. If --replace is + * given, ask to replace the old owner. + * + * @param connection the connection + * @param name the name to request + * @param flags flags + * @param error location to store the error + * @returns a result code, -1 if error is set + */ +int +dbus_bus_request_name (DBusConnection *connection, + const char *name, + unsigned int flags, + DBusError *error) +{ + DBusMessage *message, *reply; + dbus_uint32_t result; + + _dbus_return_val_if_fail (connection != NULL, 0); + _dbus_return_val_if_fail (name != NULL, 0); + _dbus_return_val_if_fail (_dbus_check_is_valid_bus_name (name), 0); + _dbus_return_val_if_error_is_set (error, 0); + + message = dbus_message_new_method_call (DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "RequestName"); + + if (message == NULL) + { + _DBUS_SET_OOM (error); + return -1; + } + + if (!dbus_message_append_args (message, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_UINT32, &flags, + DBUS_TYPE_INVALID)) + { + dbus_message_unref (message); + _DBUS_SET_OOM (error); + return -1; + } + + reply = dbus_connection_send_with_reply_and_block (connection, message, -1, + error); + + dbus_message_unref (message); + + if (reply == NULL) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + return -1; + } + + if (dbus_set_error_from_message (error, reply)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + dbus_message_unref (reply); + return -1; + } + + if (!dbus_message_get_args (reply, error, + DBUS_TYPE_UINT32, &result, + DBUS_TYPE_INVALID)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + dbus_message_unref (reply); + return -1; + } + + dbus_message_unref (reply); + + return result; +} + + +/** + * Asks the bus to unassign the given name from this connection by + * invoking the ReleaseName method on the bus. The "ReleaseName" + * method is canonically documented in the D-Bus specification. + * + * Possible results are: #DBUS_RELEASE_NAME_REPLY_RELEASED + * which means you owned the name or were in the queue to own it, + * and and now you don't own it and aren't in the queue. + * #DBUS_RELEASE_NAME_REPLY_NOT_OWNER which means someone else + * owns the name so you can't release it. + * #DBUS_RELEASE_NAME_REPLY_NON_EXISTENT + * which means nobody owned the name. + * + * @param connection the connection + * @param name the name to remove + * @param error location to store the error + * @returns a result code, -1 if error is set + */ +int +dbus_bus_release_name (DBusConnection *connection, + const char *name, + DBusError *error) +{ + DBusMessage *message, *reply; + dbus_uint32_t result; + + _dbus_return_val_if_fail (connection != NULL, 0); + _dbus_return_val_if_fail (name != NULL, 0); + _dbus_return_val_if_fail (_dbus_check_is_valid_bus_name (name), 0); + _dbus_return_val_if_error_is_set (error, 0); + + message = dbus_message_new_method_call (DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "ReleaseName"); + + if (message == NULL) + { + _DBUS_SET_OOM (error); + return -1; + } + + if (!dbus_message_append_args (message, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID)) + { + dbus_message_unref (message); + _DBUS_SET_OOM (error); + return -1; + } + + reply = dbus_connection_send_with_reply_and_block (connection, message, -1, + error); + + dbus_message_unref (message); + + if (reply == NULL) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + return -1; + } + + if (dbus_set_error_from_message (error, reply)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + dbus_message_unref (reply); + return -1; + } + + if (!dbus_message_get_args (reply, error, + DBUS_TYPE_UINT32, &result, + DBUS_TYPE_INVALID)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + dbus_message_unref (reply); + return -1; + } + + dbus_message_unref (reply); + + return result; +} + +/** + * Asks the bus whether a certain name has an owner. + * + * Using this can easily result in a race condition, + * since an owner can appear or disappear after you + * call this. + * + * If you want to request a name, just request it; + * if you want to avoid replacing a current owner, + * don't specify #DBUS_NAME_FLAG_REPLACE_EXISTING and + * you will get an error if there's already an owner. + * + * @param connection the connection + * @param name the name + * @param error location to store any errors + * @returns #TRUE if the name exists, #FALSE if not or on error + */ +dbus_bool_t +dbus_bus_name_has_owner (DBusConnection *connection, + const char *name, + DBusError *error) +{ + DBusMessage *message, *reply; + dbus_bool_t exists; + + _dbus_return_val_if_fail (connection != NULL, FALSE); + _dbus_return_val_if_fail (name != NULL, FALSE); + _dbus_return_val_if_fail (_dbus_check_is_valid_bus_name (name), FALSE); + _dbus_return_val_if_error_is_set (error, FALSE); + + message = dbus_message_new_method_call (DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "NameHasOwner"); + if (message == NULL) + { + _DBUS_SET_OOM (error); + return FALSE; + } + + if (!dbus_message_append_args (message, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID)) + { + dbus_message_unref (message); + _DBUS_SET_OOM (error); + return FALSE; + } + + reply = dbus_connection_send_with_reply_and_block (connection, message, -1, error); + dbus_message_unref (message); + + if (reply == NULL) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + return FALSE; + } + + if (!dbus_message_get_args (reply, error, + DBUS_TYPE_BOOLEAN, &exists, + DBUS_TYPE_INVALID)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + dbus_message_unref (reply); + return FALSE; + } + + dbus_message_unref (reply); + return exists; +} + +/** + * Starts a service that will request ownership of the given name. + * The returned result will be one of be one of + * #DBUS_START_REPLY_SUCCESS or #DBUS_START_REPLY_ALREADY_RUNNING if + * successful. Pass #NULL if you don't care about the result. + * + * The flags parameter is for future expansion, currently you should + * specify 0. + * + * It's often easier to avoid explicitly starting services, and + * just send a method call to the service's bus name instead. + * Method calls start a service to handle them by default + * unless you call dbus_message_set_auto_start() to disable this + * behavior. + * + * @param connection the connection + * @param name the name we want the new service to request + * @param flags the flags (should always be 0 for now) + * @param result a place to store the result or #NULL + * @param error location to store any errors + * @returns #TRUE if the activation succeeded, #FALSE if not + */ +dbus_bool_t +dbus_bus_start_service_by_name (DBusConnection *connection, + const char *name, + dbus_uint32_t flags, + dbus_uint32_t *result, + DBusError *error) +{ + DBusMessage *msg; + DBusMessage *reply; + + _dbus_return_val_if_fail (connection != NULL, FALSE); + _dbus_return_val_if_fail (_dbus_check_is_valid_bus_name (name), FALSE); + + msg = dbus_message_new_method_call (DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "StartServiceByName"); + + if (!dbus_message_append_args (msg, DBUS_TYPE_STRING, &name, + DBUS_TYPE_UINT32, &flags, DBUS_TYPE_INVALID)) + { + dbus_message_unref (msg); + _DBUS_SET_OOM (error); + return FALSE; + } + + reply = dbus_connection_send_with_reply_and_block (connection, msg, + -1, error); + dbus_message_unref (msg); + + if (reply == NULL) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + return FALSE; + } + + if (dbus_set_error_from_message (error, reply)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + dbus_message_unref (reply); + return FALSE; + } + + if (result != NULL && + !dbus_message_get_args (reply, error, DBUS_TYPE_UINT32, + result, DBUS_TYPE_INVALID)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + dbus_message_unref (reply); + return FALSE; + } + + dbus_message_unref (reply); + return TRUE; +} + +static void +send_no_return_values (DBusConnection *connection, + DBusMessage *msg, + DBusError *error) +{ + if (error) + { + /* Block to check success codepath */ + DBusMessage *reply; + + reply = dbus_connection_send_with_reply_and_block (connection, msg, + -1, error); + + if (reply == NULL) + _DBUS_ASSERT_ERROR_IS_SET (error); + else + dbus_message_unref (reply); + } + else + { + /* Silently-fail nonblocking codepath */ + dbus_message_set_no_reply (msg, TRUE); + dbus_connection_send (connection, msg, NULL); + } +} + +/** + * Adds a match rule to match messages going through the message bus. + * The "rule" argument is the string form of a match rule. + * + * If you pass #NULL for the error, this function will not + * block; the match thus won't be added until you flush the + * connection, and if there's an error adding the match + * (only possible error is lack of resources in the bus), + * you won't find out about it. + * + * If you pass non-#NULL for the error this function will + * block until it gets a reply. + * + * Normal API conventions would have the function return + * a boolean value indicating whether the error was set, + * but that would require blocking always to determine + * the return value. + * + * The AddMatch method is fully documented in the D-Bus + * specification. For quick reference, the format of the + * match rules is discussed here, but the specification + * is the canonical version of this information. + * + * Rules are specified as a string of comma separated + * key/value pairs. An example is + * "type='signal',sender='org.freedesktop.DBus', + * interface='org.freedesktop.DBus',member='Foo', + * path='/bar/foo',destination=':452345.34'" + * + * Possible keys you can match on are type, sender, + * interface, member, path, destination and numbered + * keys to match message args (keys are 'arg0', 'arg1', etc.). + * Omitting a key from the rule indicates + * a wildcard match. For instance omitting + * the member from a match rule but adding a sender would + * let all messages from that sender through regardless of + * the member. + * + * Matches are inclusive not exclusive so as long as one + * rule matches the message will get through. It is important + * to note this because every time a message is received the + * application will be paged into memory to process it. This + * can cause performance problems such as draining batteries + * on embedded platforms. + * + * If you match message args ('arg0', 'arg1', and so forth) + * only string arguments will match. That is, arg0='5' means + * match the string "5" not the integer 5. + * + * Currently there is no way to match against non-string arguments. + * + * A specialised form of wildcard matching on arguments is + * supported for path-like namespaces. If your argument match has + * a 'path' suffix (eg: "arg0path='/some/path/'") then it is + * considered a match if the argument exactly matches the given + * string or if one of them ends in a '/' and is a prefix of the + * other. + * + * Matching on interface is tricky because method call + * messages only optionally specify the interface. + * If a message omits the interface, then it will NOT match + * if the rule specifies an interface name. This means match + * rules on method calls should not usually give an interface. + * + * However, signal messages are required to include the interface + * so when matching signals usually you should specify the interface + * in the match rule. + * + * For security reasons, you can match arguments only up to + * #DBUS_MAXIMUM_MATCH_RULE_ARG_NUMBER. + * + * Match rules have a maximum length of #DBUS_MAXIMUM_MATCH_RULE_LENGTH + * bytes. + * + * Both of these maximums are much higher than you're likely to need, + * they only exist because the D-Bus bus daemon has fixed limits on + * all resource usage. + * + * @param connection connection to the message bus + * @param rule textual form of match rule + * @param error location to store any errors + */ +void +dbus_bus_add_match (DBusConnection *connection, + const char *rule, + DBusError *error) +{ + DBusMessage *msg; + + _dbus_return_if_fail (rule != NULL); + + msg = dbus_message_new_method_call (DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "AddMatch"); + + if (msg == NULL) + { + _DBUS_SET_OOM (error); + return; + } + + if (!dbus_message_append_args (msg, DBUS_TYPE_STRING, &rule, + DBUS_TYPE_INVALID)) + { + dbus_message_unref (msg); + _DBUS_SET_OOM (error); + return; + } + + send_no_return_values (connection, msg, error); + + dbus_message_unref (msg); +} + +/** + * Removes a previously-added match rule "by value" (the most + * recently-added identical rule gets removed). The "rule" argument + * is the string form of a match rule. + * + * The bus compares match rules semantically, not textually, so + * whitespace and ordering don't have to be identical to + * the rule you passed to dbus_bus_add_match(). + * + * If you pass #NULL for the error, this function will not + * block; otherwise it will. See detailed explanation in + * docs for dbus_bus_add_match(). + * + * @param connection connection to the message bus + * @param rule textual form of match rule + * @param error location to store any errors + */ +void +dbus_bus_remove_match (DBusConnection *connection, + const char *rule, + DBusError *error) +{ + DBusMessage *msg; + + _dbus_return_if_fail (rule != NULL); + + msg = dbus_message_new_method_call (DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "RemoveMatch"); + + if (!dbus_message_append_args (msg, DBUS_TYPE_STRING, &rule, + DBUS_TYPE_INVALID)) + { + dbus_message_unref (msg); + _DBUS_SET_OOM (error); + return; + } + + send_no_return_values (connection, msg, error); + + dbus_message_unref (msg); +} + +/** @} */ diff --git a/src/dbus/dbus-bus.h b/src/dbus/dbus-bus.h new file mode 100644 index 0000000..7d4f133 --- /dev/null +++ b/src/dbus/dbus-bus.h @@ -0,0 +1,82 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-bus.h Convenience functions for communicating with the bus. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION) +#error "Only can be included directly, this file may disappear or change contents." +#endif + +#ifndef DBUS_BUS_H +#define DBUS_BUS_H + +#include + +DBUS_BEGIN_DECLS + +/** + * @addtogroup DBusBus + * @{ + */ + +DBusConnection *dbus_bus_get (DBusBusType type, + DBusError *error); +DBusConnection *dbus_bus_get_private (DBusBusType type, + DBusError *error); + +dbus_bool_t dbus_bus_register (DBusConnection *connection, + DBusError *error); +dbus_bool_t dbus_bus_set_unique_name (DBusConnection *connection, + const char *unique_name); +const char* dbus_bus_get_unique_name (DBusConnection *connection); +unsigned long dbus_bus_get_unix_user (DBusConnection *connection, + const char *name, + DBusError *error); +char* dbus_bus_get_id (DBusConnection *connection, + DBusError *error); +int dbus_bus_request_name (DBusConnection *connection, + const char *name, + unsigned int flags, + DBusError *error); +int dbus_bus_release_name (DBusConnection *connection, + const char *name, + DBusError *error); +dbus_bool_t dbus_bus_name_has_owner (DBusConnection *connection, + const char *name, + DBusError *error); + +dbus_bool_t dbus_bus_start_service_by_name (DBusConnection *connection, + const char *name, + dbus_uint32_t flags, + dbus_uint32_t *reply, + DBusError *error); + +void dbus_bus_add_match (DBusConnection *connection, + const char *rule, + DBusError *error); +void dbus_bus_remove_match (DBusConnection *connection, + const char *rule, + DBusError *error); + +/** @} */ + +DBUS_END_DECLS + +#endif /* DBUS_BUS_H */ diff --git a/src/dbus/dbus-connection-internal.h b/src/dbus/dbus-connection-internal.h new file mode 100644 index 0000000..df54412 --- /dev/null +++ b/src/dbus/dbus-connection-internal.h @@ -0,0 +1,120 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-connection-internal.h DBusConnection internal interfaces + * + * Copyright (C) 2002 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef DBUS_CONNECTION_INTERNAL_H +#define DBUS_CONNECTION_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include +#include + +DBUS_BEGIN_DECLS + +typedef enum +{ + DBUS_ITERATION_DO_WRITING = 1 << 0, /**< Write messages out. */ + DBUS_ITERATION_DO_READING = 1 << 1, /**< Read messages in. */ + DBUS_ITERATION_BLOCK = 1 << 2 /**< Block if nothing to do. */ +} DBusIterationFlags; + +/** default timeout value when waiting for a message reply, 25 seconds */ +#define _DBUS_DEFAULT_TIMEOUT_VALUE (25 * 1000) + +void _dbus_connection_lock (DBusConnection *connection); +void _dbus_connection_unlock (DBusConnection *connection); +DBusConnection * _dbus_connection_ref_unlocked (DBusConnection *connection); +void _dbus_connection_unref_unlocked (DBusConnection *connection); +dbus_bool_t _dbus_connection_queue_received_message (DBusConnection *connection, + DBusMessage *message); +void _dbus_connection_queue_received_message_link (DBusConnection *connection, + DBusList *link); +dbus_bool_t _dbus_connection_has_messages_to_send_unlocked (DBusConnection *connection); +DBusMessage* _dbus_connection_get_message_to_send (DBusConnection *connection); +void _dbus_connection_message_sent (DBusConnection *connection, + DBusMessage *message); +dbus_bool_t _dbus_connection_add_watch_unlocked (DBusConnection *connection, + DBusWatch *watch); +void _dbus_connection_remove_watch_unlocked (DBusConnection *connection, + DBusWatch *watch); +void _dbus_connection_toggle_watch_unlocked (DBusConnection *connection, + DBusWatch *watch, + dbus_bool_t enabled); +dbus_bool_t _dbus_connection_handle_watch (DBusWatch *watch, + unsigned int condition, + void *data); +dbus_bool_t _dbus_connection_add_timeout_unlocked (DBusConnection *connection, + DBusTimeout *timeout); +void _dbus_connection_remove_timeout_unlocked (DBusConnection *connection, + DBusTimeout *timeout); +void _dbus_connection_toggle_timeout_unlocked (DBusConnection *connection, + DBusTimeout *timeout, + dbus_bool_t enabled); +DBusConnection* _dbus_connection_new_for_transport (DBusTransport *transport); +void _dbus_connection_do_iteration_unlocked (DBusConnection *connection, + unsigned int flags, + int timeout_milliseconds); +void _dbus_connection_close_possibly_shared (DBusConnection *connection); +void _dbus_connection_close_if_only_one_ref (DBusConnection *connection); + +DBusPendingCall* _dbus_pending_call_new (DBusConnection *connection, + int timeout_milliseconds, + DBusTimeoutHandler timeout_handler); +void _dbus_pending_call_notify (DBusPendingCall *pending); +void _dbus_connection_remove_pending_call (DBusConnection *connection, + DBusPendingCall *pending); +void _dbus_connection_block_pending_call (DBusPendingCall *pending); +void _dbus_pending_call_complete_and_unlock (DBusPendingCall *pending, + DBusMessage *message); +dbus_bool_t _dbus_connection_send_and_unlock (DBusConnection *connection, + DBusMessage *message, + dbus_uint32_t *client_serial); + +void _dbus_connection_queue_synthesized_message_link (DBusConnection *connection, + DBusList *link); +void _dbus_connection_test_get_locks (DBusConnection *conn, + DBusMutex **mutex_loc, + DBusMutex **dispatch_mutex_loc, + DBusMutex **io_path_mutex_loc, + DBusCondVar **dispatch_cond_loc, + DBusCondVar **io_path_cond_loc); + +/* This _dbus_bus_* stuff doesn't really belong here, but dbus-bus-internal.h seems + * silly for one function + */ +/** + * @addtogroup DBusBusInternals + * @{ + */ + +void _dbus_bus_notify_shared_connection_disconnected_unlocked (DBusConnection *connection); + +/** @} */ + + +DBUS_END_DECLS + +#endif /* DBUS_CONNECTION_INTERNAL_H */ diff --git a/src/dbus/dbus-connection.c b/src/dbus/dbus-connection.c new file mode 100644 index 0000000..947c0af --- /dev/null +++ b/src/dbus/dbus-connection.c @@ -0,0 +1,5901 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-connection.c DBusConnection object + * + * Copyright (C) 2002-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include "dbus-shared.h" +#include "dbus-connection.h" +#include "dbus-list.h" +#include "dbus-timeout.h" +#include "dbus-transport.h" +#include "dbus-watch.h" +#include "dbus-connection-internal.h" +#include "dbus-pending-call-internal.h" +#include "dbus-list.h" +#include "dbus-hash.h" +#include "dbus-message-internal.h" +#include "dbus-threads.h" +#include "dbus-protocol.h" +#include "dbus-dataslot.h" +#include "dbus-string.h" +#include "dbus-pending-call.h" +#include "dbus-object-tree.h" +#include "dbus-threads-internal.h" +#include "dbus-bus.h" + +#ifdef DBUS_DISABLE_CHECKS +#define TOOK_LOCK_CHECK(connection) +#define RELEASING_LOCK_CHECK(connection) +#define HAVE_LOCK_CHECK(connection) +#else +#define TOOK_LOCK_CHECK(connection) do { \ + _dbus_assert (!(connection)->have_connection_lock); \ + (connection)->have_connection_lock = TRUE; \ + } while (0) +#define RELEASING_LOCK_CHECK(connection) do { \ + _dbus_assert ((connection)->have_connection_lock); \ + (connection)->have_connection_lock = FALSE; \ + } while (0) +#define HAVE_LOCK_CHECK(connection) _dbus_assert ((connection)->have_connection_lock) +/* A "DO_NOT_HAVE_LOCK_CHECK" is impossible since we need the lock to check the flag */ +#endif + +#define TRACE_LOCKS 1 + +#define CONNECTION_LOCK(connection) do { \ + if (TRACE_LOCKS) { _dbus_verbose (" LOCK: %s\n", _DBUS_FUNCTION_NAME); } \ + _dbus_mutex_lock ((connection)->mutex); \ + TOOK_LOCK_CHECK (connection); \ + } while (0) + +#define CONNECTION_UNLOCK(connection) do { \ + if (TRACE_LOCKS) { _dbus_verbose (" UNLOCK: %s\n", _DBUS_FUNCTION_NAME); } \ + RELEASING_LOCK_CHECK (connection); \ + _dbus_mutex_unlock ((connection)->mutex); \ + } while (0) + +#define DISPATCH_STATUS_NAME(s) \ + ((s) == DBUS_DISPATCH_COMPLETE ? "complete" : \ + (s) == DBUS_DISPATCH_DATA_REMAINS ? "data remains" : \ + (s) == DBUS_DISPATCH_NEED_MEMORY ? "need memory" : \ + "???") + +/** + * @defgroup DBusConnection DBusConnection + * @ingroup DBus + * @brief Connection to another application + * + * A DBusConnection represents a connection to another + * application. Messages can be sent and received via this connection. + * The other application may be a message bus; for convenience, the + * function dbus_bus_get() is provided to automatically open a + * connection to the well-known message buses. + * + * In brief a DBusConnection is a message queue associated with some + * message transport mechanism such as a socket. The connection + * maintains a queue of incoming messages and a queue of outgoing + * messages. + * + * Several functions use the following terms: + *
    + *
  • read means to fill the incoming message queue by reading from the socket
  • + *
  • write means to drain the outgoing queue by writing to the socket
  • + *
  • dispatch means to drain the incoming queue by invoking application-provided message handlers
  • + *
+ * + * The function dbus_connection_read_write_dispatch() for example does all + * three of these things, offering a simple alternative to a main loop. + * + * In an application with a main loop, the read/write/dispatch + * operations are usually separate. + * + * The connection provides #DBusWatch and #DBusTimeout objects to + * the main loop. These are used to know when reading, writing, or + * dispatching should be performed. + * + * Incoming messages are processed + * by calling dbus_connection_dispatch(). dbus_connection_dispatch() + * runs any handlers registered for the topmost message in the message + * queue, then discards the message, then returns. + * + * dbus_connection_get_dispatch_status() indicates whether + * messages are currently in the queue that need dispatching. + * dbus_connection_set_dispatch_status_function() allows + * you to set a function to be used to monitor the dispatch status. + * + * If you're using GLib or Qt add-on libraries for D-Bus, there are + * special convenience APIs in those libraries that hide + * all the details of dispatch and watch/timeout monitoring. + * For example, dbus_connection_setup_with_g_main(). + * + * If you aren't using these add-on libraries, but want to process + * messages asynchronously, you must manually call + * dbus_connection_set_dispatch_status_function(), + * dbus_connection_set_watch_functions(), + * dbus_connection_set_timeout_functions() providing appropriate + * functions to integrate the connection with your application's main + * loop. This can be tricky to get right; main loops are not simple. + * + * If you don't need to be asynchronous, you can ignore #DBusWatch, + * #DBusTimeout, and dbus_connection_dispatch(). Instead, + * dbus_connection_read_write_dispatch() can be used. + * + * Or, in very simple applications, + * dbus_connection_pop_message() may be all you need, allowing you to + * avoid setting up any handler functions (see + * dbus_connection_add_filter(), + * dbus_connection_register_object_path() for more on handlers). + * + * When you use dbus_connection_send() or one of its variants to send + * a message, the message is added to the outgoing queue. It's + * actually written to the network later; either in + * dbus_watch_handle() invoked by your main loop, or in + * dbus_connection_flush() which blocks until it can write out the + * entire outgoing queue. The GLib/Qt add-on libraries again + * handle the details here for you by setting up watch functions. + * + * When a connection is disconnected, you are guaranteed to get a + * signal "Disconnected" from the interface + * #DBUS_INTERFACE_LOCAL, path + * #DBUS_PATH_LOCAL. + * + * You may not drop the last reference to a #DBusConnection + * until that connection has been disconnected. + * + * You may dispatch the unprocessed incoming message queue even if the + * connection is disconnected. However, "Disconnected" will always be + * the last message in the queue (obviously no messages are received + * after disconnection). + * + * After calling dbus_threads_init(), #DBusConnection has thread + * locks and drops them when invoking user callbacks, so in general is + * transparently threadsafe. However, #DBusMessage does NOT have + * thread locks; you must not send the same message to multiple + * #DBusConnection if those connections will be used from different threads, + * for example. + * + * Also, if you dispatch or pop messages from multiple threads, it + * may work in the sense that it won't crash, but it's tough to imagine + * sane results; it will be completely unpredictable which messages + * go to which threads. + * + * It's recommended to dispatch from a single thread. + * + * The most useful function to call from multiple threads at once + * is dbus_connection_send_with_reply_and_block(). That is, + * multiple threads can make method calls at the same time. + * + * If you aren't using threads, you can use a main loop and + * dbus_pending_call_set_notify() to achieve a similar result. + */ + +/** + * @defgroup DBusConnectionInternals DBusConnection implementation details + * @ingroup DBusInternals + * @brief Implementation details of DBusConnection + * + * @{ + */ + +/** + * Internal struct representing a message filter function + */ +typedef struct DBusMessageFilter DBusMessageFilter; + +/** + * Internal struct representing a message filter function + */ +struct DBusMessageFilter +{ + DBusAtomic refcount; /**< Reference count */ + DBusHandleMessageFunction function; /**< Function to call to filter */ + void *user_data; /**< User data for the function */ + DBusFreeFunction free_user_data_function; /**< Function to free the user data */ +}; + + +/** + * Internals of DBusPreallocatedSend + */ +struct DBusPreallocatedSend +{ + DBusConnection *connection; /**< Connection we'd send the message to */ + DBusList *queue_link; /**< Preallocated link in the queue */ + DBusList *counter_link; /**< Preallocated link in the resource counter */ +}; + +static dbus_bool_t _dbus_modify_sigpipe = TRUE; + +/** + * Implementation details of DBusConnection. All fields are private. + */ +struct DBusConnection +{ + DBusAtomic refcount; /**< Reference count. */ + + DBusMutex *mutex; /**< Lock on the entire DBusConnection */ + + DBusMutex *dispatch_mutex; /**< Protects dispatch_acquired */ + DBusCondVar *dispatch_cond; /**< Notify when dispatch_acquired is available */ + DBusMutex *io_path_mutex; /**< Protects io_path_acquired */ + DBusCondVar *io_path_cond; /**< Notify when io_path_acquired is available */ + + DBusList *outgoing_messages; /**< Queue of messages we need to send, send the end of the list first. */ + DBusList *incoming_messages; /**< Queue of messages we have received, end of the list received most recently. */ + + DBusMessage *message_borrowed; /**< Filled in if the first incoming message has been borrowed; + * dispatch_acquired will be set by the borrower + */ + + int n_outgoing; /**< Length of outgoing queue. */ + int n_incoming; /**< Length of incoming queue. */ + + DBusCounter *outgoing_counter; /**< Counts size of outgoing messages. */ + + DBusTransport *transport; /**< Object that sends/receives messages over network. */ + DBusWatchList *watches; /**< Stores active watches. */ + DBusTimeoutList *timeouts; /**< Stores active timeouts. */ + + DBusList *filter_list; /**< List of filters. */ + + DBusDataSlotList slot_list; /**< Data stored by allocated integer ID */ + + DBusHashTable *pending_replies; /**< Hash of message serials to #DBusPendingCall. */ + + dbus_uint32_t client_serial; /**< Client serial. Increments each time a message is sent */ + DBusList *disconnect_message_link; /**< Preallocated list node for queueing the disconnection message */ + + DBusWakeupMainFunction wakeup_main_function; /**< Function to wake up the mainloop */ + void *wakeup_main_data; /**< Application data for wakeup_main_function */ + DBusFreeFunction free_wakeup_main_data; /**< free wakeup_main_data */ + + DBusDispatchStatusFunction dispatch_status_function; /**< Function on dispatch status changes */ + void *dispatch_status_data; /**< Application data for dispatch_status_function */ + DBusFreeFunction free_dispatch_status_data; /**< free dispatch_status_data */ + + DBusDispatchStatus last_dispatch_status; /**< The last dispatch status we reported to the application. */ + + DBusList *link_cache; /**< A cache of linked list links to prevent contention + * for the global linked list mempool lock + */ + DBusObjectTree *objects; /**< Object path handlers registered with this connection */ + + char *server_guid; /**< GUID of server if we are in shared_connections, #NULL if server GUID is unknown or connection is private */ + + /* These two MUST be bools and not bitfields, because they are protected by a separate lock + * from connection->mutex and all bitfields in a word have to be read/written together. + * So you can't have a different lock for different bitfields in the same word. + */ + dbus_bool_t dispatch_acquired; /**< Someone has dispatch path (can drain incoming queue) */ + dbus_bool_t io_path_acquired; /**< Someone has transport io path (can use the transport to read/write messages) */ + + unsigned int shareable : 1; /**< #TRUE if libdbus owns a reference to the connection and can return it from dbus_connection_open() more than once */ + + unsigned int exit_on_disconnect : 1; /**< If #TRUE, exit after handling disconnect signal */ + + unsigned int route_peer_messages : 1; /**< If #TRUE, if org.freedesktop.DBus.Peer messages have a bus name, don't handle them automatically */ + + unsigned int disconnected_message_arrived : 1; /**< We popped or are dispatching the disconnected message. + * if the disconnect_message_link is NULL then we queued it, but + * this flag is whether it got to the head of the queue. + */ + unsigned int disconnected_message_processed : 1; /**< We did our default handling of the disconnected message, + * such as closing the connection. + */ + +#ifndef DBUS_DISABLE_CHECKS + unsigned int have_connection_lock : 1; /**< Used to check locking */ +#endif + +#ifndef DBUS_DISABLE_CHECKS + int generation; /**< _dbus_current_generation that should correspond to this connection */ +#endif +}; + +static DBusDispatchStatus _dbus_connection_get_dispatch_status_unlocked (DBusConnection *connection); +static void _dbus_connection_update_dispatch_status_and_unlock (DBusConnection *connection, + DBusDispatchStatus new_status); +static void _dbus_connection_last_unref (DBusConnection *connection); +static void _dbus_connection_acquire_dispatch (DBusConnection *connection); +static void _dbus_connection_release_dispatch (DBusConnection *connection); +static DBusDispatchStatus _dbus_connection_flush_unlocked (DBusConnection *connection); +static void _dbus_connection_close_possibly_shared_and_unlock (DBusConnection *connection); +static dbus_bool_t _dbus_connection_get_is_connected_unlocked (DBusConnection *connection); + +static DBusMessageFilter * +_dbus_message_filter_ref (DBusMessageFilter *filter) +{ + _dbus_assert (filter->refcount.value > 0); + _dbus_atomic_inc (&filter->refcount); + + return filter; +} + +static void +_dbus_message_filter_unref (DBusMessageFilter *filter) +{ + _dbus_assert (filter->refcount.value > 0); + + if (_dbus_atomic_dec (&filter->refcount) == 1) + { + if (filter->free_user_data_function) + (* filter->free_user_data_function) (filter->user_data); + + dbus_free (filter); + } +} + +/** + * Acquires the connection lock. + * + * @param connection the connection. + */ +void +_dbus_connection_lock (DBusConnection *connection) +{ + CONNECTION_LOCK (connection); +} + +/** + * Releases the connection lock. + * + * @param connection the connection. + */ +void +_dbus_connection_unlock (DBusConnection *connection) +{ + CONNECTION_UNLOCK (connection); +} + +/** + * Wakes up the main loop if it is sleeping + * Needed if we're e.g. queueing outgoing messages + * on a thread while the mainloop sleeps. + * + * @param connection the connection. + */ +static void +_dbus_connection_wakeup_mainloop (DBusConnection *connection) +{ + if (connection->wakeup_main_function) + (*connection->wakeup_main_function) (connection->wakeup_main_data); +} + +#ifdef DBUS_BUILD_TESTS +/* For now this function isn't used */ +/** + * Adds a message to the incoming message queue, returning #FALSE + * if there's insufficient memory to queue the message. + * Does not take over refcount of the message. + * + * @param connection the connection. + * @param message the message to queue. + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_connection_queue_received_message (DBusConnection *connection, + DBusMessage *message) +{ + DBusList *link; + + link = _dbus_list_alloc_link (message); + if (link == NULL) + return FALSE; + + dbus_message_ref (message); + _dbus_connection_queue_received_message_link (connection, link); + + return TRUE; +} + +/** + * Gets the locks so we can examine them + * + * @param connection the connection. + * @param mutex_loc return for the location of the main mutex pointer + * @param dispatch_mutex_loc return location of the dispatch mutex pointer + * @param io_path_mutex_loc return location of the io_path mutex pointer + * @param dispatch_cond_loc return location of the dispatch conditional + * variable pointer + * @param io_path_cond_loc return location of the io_path conditional + * variable pointer + */ +void +_dbus_connection_test_get_locks (DBusConnection *connection, + DBusMutex **mutex_loc, + DBusMutex **dispatch_mutex_loc, + DBusMutex **io_path_mutex_loc, + DBusCondVar **dispatch_cond_loc, + DBusCondVar **io_path_cond_loc) +{ + *mutex_loc = connection->mutex; + *dispatch_mutex_loc = connection->dispatch_mutex; + *io_path_mutex_loc = connection->io_path_mutex; + *dispatch_cond_loc = connection->dispatch_cond; + *io_path_cond_loc = connection->io_path_cond; +} +#endif + +/** + * Adds a message-containing list link to the incoming message queue, + * taking ownership of the link and the message's current refcount. + * Cannot fail due to lack of memory. + * + * @param connection the connection. + * @param link the message link to queue. + */ +void +_dbus_connection_queue_received_message_link (DBusConnection *connection, + DBusList *link) +{ + DBusPendingCall *pending; + dbus_uint32_t reply_serial; + DBusMessage *message; + + _dbus_assert (_dbus_transport_get_is_authenticated (connection->transport)); + + _dbus_list_append_link (&connection->incoming_messages, + link); + message = link->data; + + /* If this is a reply we're waiting on, remove timeout for it */ + reply_serial = dbus_message_get_reply_serial (message); + if (reply_serial != 0) + { + pending = _dbus_hash_table_lookup_int (connection->pending_replies, + reply_serial); + if (pending != NULL) + { + if (_dbus_pending_call_is_timeout_added_unlocked (pending)) + _dbus_connection_remove_timeout_unlocked (connection, + _dbus_pending_call_get_timeout_unlocked (pending)); + + _dbus_pending_call_set_timeout_added_unlocked (pending, FALSE); + } + } + + + + connection->n_incoming += 1; + + _dbus_connection_wakeup_mainloop (connection); + + _dbus_verbose ("Message %p (%d %s %s %s '%s' reply to %u) added to incoming queue %p, %d incoming\n", + message, + dbus_message_get_type (message), + dbus_message_get_path (message) ? + dbus_message_get_path (message) : + "no path", + 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_signature (message), + dbus_message_get_reply_serial (message), + connection, + connection->n_incoming);} + +/** + * Adds a link + message to the incoming message queue. + * Can't fail. Takes ownership of both link and message. + * + * @param connection the connection. + * @param link the list node and message to queue. + * + */ +void +_dbus_connection_queue_synthesized_message_link (DBusConnection *connection, + DBusList *link) +{ + HAVE_LOCK_CHECK (connection); + + _dbus_list_append_link (&connection->incoming_messages, link); + + connection->n_incoming += 1; + + _dbus_connection_wakeup_mainloop (connection); + + _dbus_verbose ("Synthesized message %p added to incoming queue %p, %d incoming\n", + link->data, connection, connection->n_incoming); +} + + +/** + * Checks whether there are messages in the outgoing message queue. + * Called with connection lock held. + * + * @param connection the connection. + * @returns #TRUE if the outgoing queue is non-empty. + */ +dbus_bool_t +_dbus_connection_has_messages_to_send_unlocked (DBusConnection *connection) +{ + HAVE_LOCK_CHECK (connection); + return connection->outgoing_messages != NULL; +} + +/** + * Checks whether there are messages in the outgoing message queue. + * Use dbus_connection_flush() to block until all outgoing + * messages have been written to the underlying transport + * (such as a socket). + * + * @param connection the connection. + * @returns #TRUE if the outgoing queue is non-empty. + */ +dbus_bool_t +dbus_connection_has_messages_to_send (DBusConnection *connection) +{ + dbus_bool_t v; + + _dbus_return_val_if_fail (connection != NULL, FALSE); + + CONNECTION_LOCK (connection); + v = _dbus_connection_has_messages_to_send_unlocked (connection); + CONNECTION_UNLOCK (connection); + + return v; +} + +/** + * Gets the next outgoing message. The message remains in the + * queue, and the caller does not own a reference to it. + * + * @param connection the connection. + * @returns the message to be sent. + */ +DBusMessage* +_dbus_connection_get_message_to_send (DBusConnection *connection) +{ + HAVE_LOCK_CHECK (connection); + + return _dbus_list_get_last (&connection->outgoing_messages); +} + +/** + * Notifies the connection that a message has been sent, so the + * message can be removed from the outgoing queue. + * Called with the connection lock held. + * + * @param connection the connection. + * @param message the message that was sent. + */ +void +_dbus_connection_message_sent (DBusConnection *connection, + DBusMessage *message) +{ + DBusList *link; + + HAVE_LOCK_CHECK (connection); + + /* This can be called before we even complete authentication, since + * it's called on disconnect to clean up the outgoing queue. + * It's also called as we successfully send each message. + */ + + link = _dbus_list_get_last_link (&connection->outgoing_messages); + _dbus_assert (link != NULL); + _dbus_assert (link->data == message); + + /* Save this link in the link cache */ + _dbus_list_unlink (&connection->outgoing_messages, + link); + _dbus_list_prepend_link (&connection->link_cache, link); + + connection->n_outgoing -= 1; + + _dbus_verbose ("Message %p (%d %s %s %s '%s') removed from outgoing queue %p, %d left to send\n", + message, + dbus_message_get_type (message), + dbus_message_get_path (message) ? + dbus_message_get_path (message) : + "no path", + 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_signature (message), + connection, connection->n_outgoing); + + /* Save this link in the link cache also */ + _dbus_message_remove_size_counter (message, connection->outgoing_counter, + &link); + _dbus_list_prepend_link (&connection->link_cache, link); + + dbus_message_unref (message); +} + +/** Function to be called in protected_change_watch() with refcount held */ +typedef dbus_bool_t (* DBusWatchAddFunction) (DBusWatchList *list, + DBusWatch *watch); +/** Function to be called in protected_change_watch() with refcount held */ +typedef void (* DBusWatchRemoveFunction) (DBusWatchList *list, + DBusWatch *watch); +/** Function to be called in protected_change_watch() with refcount held */ +typedef void (* DBusWatchToggleFunction) (DBusWatchList *list, + DBusWatch *watch, + dbus_bool_t enabled); + +static dbus_bool_t +protected_change_watch (DBusConnection *connection, + DBusWatch *watch, + DBusWatchAddFunction add_function, + DBusWatchRemoveFunction remove_function, + DBusWatchToggleFunction toggle_function, + dbus_bool_t enabled) +{ + DBusWatchList *watches; + dbus_bool_t retval; + + HAVE_LOCK_CHECK (connection); + + /* This isn't really safe or reasonable; a better pattern is the "do everything, then + * drop lock and call out" one; but it has to be propagated up through all callers + */ + + watches = connection->watches; + if (watches) + { + connection->watches = NULL; + _dbus_connection_ref_unlocked (connection); + CONNECTION_UNLOCK (connection); + + if (add_function) + retval = (* add_function) (watches, watch); + else if (remove_function) + { + retval = TRUE; + (* remove_function) (watches, watch); + } + else + { + retval = TRUE; + (* toggle_function) (watches, watch, enabled); + } + + CONNECTION_LOCK (connection); + connection->watches = watches; + _dbus_connection_unref_unlocked (connection); + + return retval; + } + else + return FALSE; +} + + +/** + * Adds a watch using the connection's DBusAddWatchFunction if + * available. Otherwise records the watch to be added when said + * function is available. Also re-adds the watch if the + * DBusAddWatchFunction changes. May fail due to lack of memory. + * Connection lock should be held when calling this. + * + * @param connection the connection. + * @param watch the watch to add. + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_connection_add_watch_unlocked (DBusConnection *connection, + DBusWatch *watch) +{ + return protected_change_watch (connection, watch, + _dbus_watch_list_add_watch, + NULL, NULL, FALSE); +} + +/** + * Removes a watch using the connection's DBusRemoveWatchFunction + * if available. It's an error to call this function on a watch + * that was not previously added. + * Connection lock should be held when calling this. + * + * @param connection the connection. + * @param watch the watch to remove. + */ +void +_dbus_connection_remove_watch_unlocked (DBusConnection *connection, + DBusWatch *watch) +{ + protected_change_watch (connection, watch, + NULL, + _dbus_watch_list_remove_watch, + NULL, FALSE); +} + +/** + * Toggles a watch and notifies app via connection's + * DBusWatchToggledFunction if available. It's an error to call this + * function on a watch that was not previously added. + * Connection lock should be held when calling this. + * + * @param connection the connection. + * @param watch the watch to toggle. + * @param enabled whether to enable or disable + */ +void +_dbus_connection_toggle_watch_unlocked (DBusConnection *connection, + DBusWatch *watch, + dbus_bool_t enabled) +{ + _dbus_assert (watch != NULL); + + protected_change_watch (connection, watch, + NULL, NULL, + _dbus_watch_list_toggle_watch, + enabled); +} + +/** Function to be called in protected_change_timeout() with refcount held */ +typedef dbus_bool_t (* DBusTimeoutAddFunction) (DBusTimeoutList *list, + DBusTimeout *timeout); +/** Function to be called in protected_change_timeout() with refcount held */ +typedef void (* DBusTimeoutRemoveFunction) (DBusTimeoutList *list, + DBusTimeout *timeout); +/** Function to be called in protected_change_timeout() with refcount held */ +typedef void (* DBusTimeoutToggleFunction) (DBusTimeoutList *list, + DBusTimeout *timeout, + dbus_bool_t enabled); + +static dbus_bool_t +protected_change_timeout (DBusConnection *connection, + DBusTimeout *timeout, + DBusTimeoutAddFunction add_function, + DBusTimeoutRemoveFunction remove_function, + DBusTimeoutToggleFunction toggle_function, + dbus_bool_t enabled) +{ + DBusTimeoutList *timeouts; + dbus_bool_t retval; + + HAVE_LOCK_CHECK (connection); + + /* This isn't really safe or reasonable; a better pattern is the "do everything, then + * drop lock and call out" one; but it has to be propagated up through all callers + */ + + timeouts = connection->timeouts; + if (timeouts) + { + connection->timeouts = NULL; + _dbus_connection_ref_unlocked (connection); + CONNECTION_UNLOCK (connection); + + if (add_function) + retval = (* add_function) (timeouts, timeout); + else if (remove_function) + { + retval = TRUE; + (* remove_function) (timeouts, timeout); + } + else + { + retval = TRUE; + (* toggle_function) (timeouts, timeout, enabled); + } + + CONNECTION_LOCK (connection); + connection->timeouts = timeouts; + _dbus_connection_unref_unlocked (connection); + + return retval; + } + else + return FALSE; +} + +/** + * Adds a timeout using the connection's DBusAddTimeoutFunction if + * available. Otherwise records the timeout to be added when said + * function is available. Also re-adds the timeout if the + * DBusAddTimeoutFunction changes. May fail due to lack of memory. + * The timeout will fire repeatedly until removed. + * Connection lock should be held when calling this. + * + * @param connection the connection. + * @param timeout the timeout to add. + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_connection_add_timeout_unlocked (DBusConnection *connection, + DBusTimeout *timeout) +{ + return protected_change_timeout (connection, timeout, + _dbus_timeout_list_add_timeout, + NULL, NULL, FALSE); +} + +/** + * Removes a timeout using the connection's DBusRemoveTimeoutFunction + * if available. It's an error to call this function on a timeout + * that was not previously added. + * Connection lock should be held when calling this. + * + * @param connection the connection. + * @param timeout the timeout to remove. + */ +void +_dbus_connection_remove_timeout_unlocked (DBusConnection *connection, + DBusTimeout *timeout) +{ + protected_change_timeout (connection, timeout, + NULL, + _dbus_timeout_list_remove_timeout, + NULL, FALSE); +} + +/** + * Toggles a timeout and notifies app via connection's + * DBusTimeoutToggledFunction if available. It's an error to call this + * function on a timeout that was not previously added. + * Connection lock should be held when calling this. + * + * @param connection the connection. + * @param timeout the timeout to toggle. + * @param enabled whether to enable or disable + */ +void +_dbus_connection_toggle_timeout_unlocked (DBusConnection *connection, + DBusTimeout *timeout, + dbus_bool_t enabled) +{ + protected_change_timeout (connection, timeout, + NULL, NULL, + _dbus_timeout_list_toggle_timeout, + enabled); +} + +static dbus_bool_t +_dbus_connection_attach_pending_call_unlocked (DBusConnection *connection, + DBusPendingCall *pending) +{ + dbus_uint32_t reply_serial; + DBusTimeout *timeout; + + HAVE_LOCK_CHECK (connection); + + reply_serial = _dbus_pending_call_get_reply_serial_unlocked (pending); + + _dbus_assert (reply_serial != 0); + + timeout = _dbus_pending_call_get_timeout_unlocked (pending); + + if (!_dbus_connection_add_timeout_unlocked (connection, timeout)) + return FALSE; + + if (!_dbus_hash_table_insert_int (connection->pending_replies, + reply_serial, + pending)) + { + _dbus_connection_remove_timeout_unlocked (connection, timeout); + + _dbus_pending_call_set_timeout_added_unlocked (pending, FALSE); + HAVE_LOCK_CHECK (connection); + return FALSE; + } + + _dbus_pending_call_set_timeout_added_unlocked (pending, TRUE); + + _dbus_pending_call_ref_unlocked (pending); + + HAVE_LOCK_CHECK (connection); + + return TRUE; +} + +static void +free_pending_call_on_hash_removal (void *data) +{ + DBusPendingCall *pending; + DBusConnection *connection; + + if (data == NULL) + return; + + pending = data; + + connection = _dbus_pending_call_get_connection_unlocked (pending); + + HAVE_LOCK_CHECK (connection); + + if (_dbus_pending_call_is_timeout_added_unlocked (pending)) + { + _dbus_connection_remove_timeout_unlocked (connection, + _dbus_pending_call_get_timeout_unlocked (pending)); + + _dbus_pending_call_set_timeout_added_unlocked (pending, FALSE); + } + + /* FIXME 1.0? this is sort of dangerous and undesirable to drop the lock + * here, but the pending call finalizer could in principle call out to + * application code so we pretty much have to... some larger code reorg + * might be needed. + */ + _dbus_connection_ref_unlocked (connection); + _dbus_pending_call_unref_and_unlock (pending); + CONNECTION_LOCK (connection); + _dbus_connection_unref_unlocked (connection); +} + +static void +_dbus_connection_detach_pending_call_unlocked (DBusConnection *connection, + DBusPendingCall *pending) +{ + /* This ends up unlocking to call the pending call finalizer, which is unexpected to + * say the least. + */ + _dbus_hash_table_remove_int (connection->pending_replies, + _dbus_pending_call_get_reply_serial_unlocked (pending)); +} + +static void +_dbus_connection_detach_pending_call_and_unlock (DBusConnection *connection, + DBusPendingCall *pending) +{ + /* The idea here is to avoid finalizing the pending call + * with the lock held, since there's a destroy notifier + * in pending call that goes out to application code. + * + * There's an extra unlock inside the hash table + * "free pending call" function FIXME... + */ + _dbus_pending_call_ref_unlocked (pending); + _dbus_hash_table_remove_int (connection->pending_replies, + _dbus_pending_call_get_reply_serial_unlocked (pending)); + + if (_dbus_pending_call_is_timeout_added_unlocked (pending)) + _dbus_connection_remove_timeout_unlocked (connection, + _dbus_pending_call_get_timeout_unlocked (pending)); + + _dbus_pending_call_set_timeout_added_unlocked (pending, FALSE); + + _dbus_pending_call_unref_and_unlock (pending); +} + +/** + * Removes a pending call from the connection, such that + * the pending reply will be ignored. May drop the last + * reference to the pending call. + * + * @param connection the connection + * @param pending the pending call + */ +void +_dbus_connection_remove_pending_call (DBusConnection *connection, + DBusPendingCall *pending) +{ + CONNECTION_LOCK (connection); + _dbus_connection_detach_pending_call_and_unlock (connection, pending); +} + +/** + * Acquire the transporter I/O path. This must be done before + * doing any I/O in the transporter. May sleep and drop the + * IO path mutex while waiting for the I/O path. + * + * @param connection the connection. + * @param timeout_milliseconds maximum blocking time, or -1 for no limit. + * @returns TRUE if the I/O path was acquired. + */ +static dbus_bool_t +_dbus_connection_acquire_io_path (DBusConnection *connection, + int timeout_milliseconds) +{ + dbus_bool_t we_acquired; + + HAVE_LOCK_CHECK (connection); + + /* We don't want the connection to vanish */ + _dbus_connection_ref_unlocked (connection); + + /* We will only touch io_path_acquired which is protected by our mutex */ + CONNECTION_UNLOCK (connection); + + _dbus_verbose ("%s locking io_path_mutex\n", _DBUS_FUNCTION_NAME); + _dbus_mutex_lock (connection->io_path_mutex); + + _dbus_verbose ("%s start connection->io_path_acquired = %d timeout = %d\n", + _DBUS_FUNCTION_NAME, connection->io_path_acquired, timeout_milliseconds); + + we_acquired = FALSE; + + if (connection->io_path_acquired) + { + if (timeout_milliseconds != -1) + { + _dbus_verbose ("%s waiting %d for IO path to be acquirable\n", + _DBUS_FUNCTION_NAME, timeout_milliseconds); + + if (!_dbus_condvar_wait_timeout (connection->io_path_cond, + connection->io_path_mutex, + timeout_milliseconds)) + { + /* We timed out before anyone signaled. */ + /* (writing the loop to handle the !timedout case by + * waiting longer if needed is a pain since dbus + * wraps pthread_cond_timedwait to take a relative + * time instead of absolute, something kind of stupid + * on our part. for now it doesn't matter, we will just + * end up back here eventually.) + */ + } + } + else + { + while (connection->io_path_acquired) + { + _dbus_verbose ("%s waiting for IO path to be acquirable\n", _DBUS_FUNCTION_NAME); + _dbus_condvar_wait (connection->io_path_cond, + connection->io_path_mutex); + } + } + } + + if (!connection->io_path_acquired) + { + we_acquired = TRUE; + connection->io_path_acquired = TRUE; + } + + _dbus_verbose ("%s end connection->io_path_acquired = %d we_acquired = %d\n", + _DBUS_FUNCTION_NAME, connection->io_path_acquired, we_acquired); + + _dbus_verbose ("%s unlocking io_path_mutex\n", _DBUS_FUNCTION_NAME); + _dbus_mutex_unlock (connection->io_path_mutex); + + CONNECTION_LOCK (connection); + + HAVE_LOCK_CHECK (connection); + + _dbus_connection_unref_unlocked (connection); + + return we_acquired; +} + +/** + * Release the I/O path when you're done with it. Only call + * after you've acquired the I/O. Wakes up at most one thread + * currently waiting to acquire the I/O path. + * + * @param connection the connection. + */ +static void +_dbus_connection_release_io_path (DBusConnection *connection) +{ + HAVE_LOCK_CHECK (connection); + + _dbus_verbose ("%s locking io_path_mutex\n", _DBUS_FUNCTION_NAME); + _dbus_mutex_lock (connection->io_path_mutex); + + _dbus_assert (connection->io_path_acquired); + + _dbus_verbose ("%s start connection->io_path_acquired = %d\n", + _DBUS_FUNCTION_NAME, connection->io_path_acquired); + + connection->io_path_acquired = FALSE; + _dbus_condvar_wake_one (connection->io_path_cond); + + _dbus_verbose ("%s unlocking io_path_mutex\n", _DBUS_FUNCTION_NAME); + _dbus_mutex_unlock (connection->io_path_mutex); +} + +/** + * Queues incoming messages and sends outgoing messages for this + * connection, optionally blocking in the process. Each call to + * _dbus_connection_do_iteration_unlocked() will call select() or poll() one + * time and then read or write data if possible. + * + * The purpose of this function is to be able to flush outgoing + * messages or queue up incoming messages without returning + * control to the application and causing reentrancy weirdness. + * + * The flags parameter allows you to specify whether to + * read incoming messages, write outgoing messages, or both, + * and whether to block if no immediate action is possible. + * + * The timeout_milliseconds parameter does nothing unless the + * iteration is blocking. + * + * If there are no outgoing messages and DBUS_ITERATION_DO_READING + * wasn't specified, then it's impossible to block, even if + * you specify DBUS_ITERATION_BLOCK; in that case the function + * returns immediately. + * + * Called with connection lock held. + * + * @param connection the connection. + * @param flags iteration flags. + * @param timeout_milliseconds maximum blocking time, or -1 for no limit. + */ +void +_dbus_connection_do_iteration_unlocked (DBusConnection *connection, + unsigned int flags, + int timeout_milliseconds) +{ + _dbus_verbose ("%s start\n", _DBUS_FUNCTION_NAME); + + HAVE_LOCK_CHECK (connection); + + if (connection->n_outgoing == 0) + flags &= ~DBUS_ITERATION_DO_WRITING; + + if (_dbus_connection_acquire_io_path (connection, + (flags & DBUS_ITERATION_BLOCK) ? timeout_milliseconds : 0)) + { + HAVE_LOCK_CHECK (connection); + + _dbus_transport_do_iteration (connection->transport, + flags, timeout_milliseconds); + _dbus_connection_release_io_path (connection); + } + + HAVE_LOCK_CHECK (connection); + + _dbus_verbose ("%s end\n", _DBUS_FUNCTION_NAME); +} + +/** + * Creates a new connection for the given transport. A transport + * represents a message stream that uses some concrete mechanism, such + * as UNIX domain sockets. May return #NULL if insufficient + * memory exists to create the connection. + * + * @param transport the transport. + * @returns the new connection, or #NULL on failure. + */ +DBusConnection* +_dbus_connection_new_for_transport (DBusTransport *transport) +{ + DBusConnection *connection; + DBusWatchList *watch_list; + DBusTimeoutList *timeout_list; + DBusHashTable *pending_replies; + DBusList *disconnect_link; + DBusMessage *disconnect_message; + DBusCounter *outgoing_counter; + DBusObjectTree *objects; + + watch_list = NULL; + connection = NULL; + pending_replies = NULL; + timeout_list = NULL; + disconnect_link = NULL; + disconnect_message = NULL; + outgoing_counter = NULL; + objects = NULL; + + watch_list = _dbus_watch_list_new (); + if (watch_list == NULL) + goto error; + + timeout_list = _dbus_timeout_list_new (); + if (timeout_list == NULL) + goto error; + + pending_replies = + _dbus_hash_table_new (DBUS_HASH_INT, + NULL, + (DBusFreeFunction)free_pending_call_on_hash_removal); + if (pending_replies == NULL) + goto error; + + connection = dbus_new0 (DBusConnection, 1); + if (connection == NULL) + goto error; + + _dbus_mutex_new_at_location (&connection->mutex); + if (connection->mutex == NULL) + goto error; + + _dbus_mutex_new_at_location (&connection->io_path_mutex); + if (connection->io_path_mutex == NULL) + goto error; + + _dbus_mutex_new_at_location (&connection->dispatch_mutex); + if (connection->dispatch_mutex == NULL) + goto error; + + _dbus_condvar_new_at_location (&connection->dispatch_cond); + if (connection->dispatch_cond == NULL) + goto error; + + _dbus_condvar_new_at_location (&connection->io_path_cond); + if (connection->io_path_cond == NULL) + goto error; + + disconnect_message = dbus_message_new_signal (DBUS_PATH_LOCAL, + DBUS_INTERFACE_LOCAL, + "Disconnected"); + + if (disconnect_message == NULL) + goto error; + + disconnect_link = _dbus_list_alloc_link (disconnect_message); + if (disconnect_link == NULL) + goto error; + + outgoing_counter = _dbus_counter_new (); + if (outgoing_counter == NULL) + goto error; + + objects = _dbus_object_tree_new (connection); + if (objects == NULL) + goto error; + + if (_dbus_modify_sigpipe) + _dbus_disable_sigpipe (); + + connection->refcount.value = 1; + connection->transport = transport; + connection->watches = watch_list; + connection->timeouts = timeout_list; + connection->pending_replies = pending_replies; + connection->outgoing_counter = outgoing_counter; + connection->filter_list = NULL; + connection->last_dispatch_status = DBUS_DISPATCH_COMPLETE; /* so we're notified first time there's data */ + connection->objects = objects; + connection->exit_on_disconnect = FALSE; + connection->shareable = FALSE; + connection->route_peer_messages = FALSE; + connection->disconnected_message_arrived = FALSE; + connection->disconnected_message_processed = FALSE; + +#ifndef DBUS_DISABLE_CHECKS + connection->generation = _dbus_current_generation; +#endif + + _dbus_data_slot_list_init (&connection->slot_list); + + connection->client_serial = 1; + + connection->disconnect_message_link = disconnect_link; + + CONNECTION_LOCK (connection); + + if (!_dbus_transport_set_connection (transport, connection)) + { + CONNECTION_UNLOCK (connection); + + goto error; + } + + _dbus_transport_ref (transport); + + CONNECTION_UNLOCK (connection); + + return connection; + + error: + if (disconnect_message != NULL) + dbus_message_unref (disconnect_message); + + if (disconnect_link != NULL) + _dbus_list_free_link (disconnect_link); + + if (connection != NULL) + { + _dbus_condvar_free_at_location (&connection->io_path_cond); + _dbus_condvar_free_at_location (&connection->dispatch_cond); + _dbus_mutex_free_at_location (&connection->mutex); + _dbus_mutex_free_at_location (&connection->io_path_mutex); + _dbus_mutex_free_at_location (&connection->dispatch_mutex); + dbus_free (connection); + } + if (pending_replies) + _dbus_hash_table_unref (pending_replies); + + if (watch_list) + _dbus_watch_list_free (watch_list); + + if (timeout_list) + _dbus_timeout_list_free (timeout_list); + + if (outgoing_counter) + _dbus_counter_unref (outgoing_counter); + + if (objects) + _dbus_object_tree_unref (objects); + + return NULL; +} + +/** + * Increments the reference count of a DBusConnection. + * Requires that the caller already holds the connection lock. + * + * @param connection the connection. + * @returns the connection. + */ +DBusConnection * +_dbus_connection_ref_unlocked (DBusConnection *connection) +{ + _dbus_assert (connection != NULL); + _dbus_assert (connection->generation == _dbus_current_generation); + + HAVE_LOCK_CHECK (connection); + +#ifdef DBUS_HAVE_ATOMIC_INT + _dbus_atomic_inc (&connection->refcount); +#else + _dbus_assert (connection->refcount.value > 0); + connection->refcount.value += 1; +#endif + + return connection; +} + +/** + * Decrements the reference count of a DBusConnection. + * Requires that the caller already holds the connection lock. + * + * @param connection the connection. + */ +void +_dbus_connection_unref_unlocked (DBusConnection *connection) +{ + dbus_bool_t last_unref; + + HAVE_LOCK_CHECK (connection); + + _dbus_assert (connection != NULL); + + /* The connection lock is better than the global + * lock in the atomic increment fallback + */ + +#ifdef DBUS_HAVE_ATOMIC_INT + last_unref = (_dbus_atomic_dec (&connection->refcount) == 1); +#else + _dbus_assert (connection->refcount.value > 0); + + connection->refcount.value -= 1; + last_unref = (connection->refcount.value == 0); +#if 0 + printf ("unref_unlocked() connection %p count = %d\n", connection, connection->refcount.value); +#endif +#endif + + if (last_unref) + _dbus_connection_last_unref (connection); +} + +static dbus_uint32_t +_dbus_connection_get_next_client_serial (DBusConnection *connection) +{ + dbus_uint32_t serial; + + serial = connection->client_serial++; + + if (connection->client_serial == 0) + connection->client_serial = 1; + + return serial; +} + +/** + * A callback for use with dbus_watch_new() to create a DBusWatch. + * + * @todo This is basically a hack - we could delete _dbus_transport_handle_watch() + * and the virtual handle_watch in DBusTransport if we got rid of it. + * The reason this is some work is threading, see the _dbus_connection_handle_watch() + * implementation. + * + * @param watch the watch. + * @param condition the current condition of the file descriptors being watched. + * @param data must be a pointer to a #DBusConnection + * @returns #FALSE if the IO condition may not have been fully handled due to lack of memory + */ +dbus_bool_t +_dbus_connection_handle_watch (DBusWatch *watch, + unsigned int condition, + void *data) +{ + DBusConnection *connection; + dbus_bool_t retval; + DBusDispatchStatus status; + + connection = data; + + _dbus_verbose ("%s start\n", _DBUS_FUNCTION_NAME); + + CONNECTION_LOCK (connection); + _dbus_connection_acquire_io_path (connection, -1); + HAVE_LOCK_CHECK (connection); + retval = _dbus_transport_handle_watch (connection->transport, + watch, condition); + + _dbus_connection_release_io_path (connection); + + HAVE_LOCK_CHECK (connection); + + _dbus_verbose ("%s middle\n", _DBUS_FUNCTION_NAME); + + status = _dbus_connection_get_dispatch_status_unlocked (connection); + + /* this calls out to user code */ + _dbus_connection_update_dispatch_status_and_unlock (connection, status); + + _dbus_verbose ("%s end\n", _DBUS_FUNCTION_NAME); + + return retval; +} + +_DBUS_DEFINE_GLOBAL_LOCK (shared_connections); +static DBusHashTable *shared_connections = NULL; +static DBusList *shared_connections_no_guid = NULL; + +static void +close_connection_on_shutdown (DBusConnection *connection) +{ + DBusMessage *message; + + dbus_connection_ref (connection); + _dbus_connection_close_possibly_shared (connection); + + /* Churn through to the Disconnected message */ + while ((message = dbus_connection_pop_message (connection))) + { + dbus_message_unref (message); + } + dbus_connection_unref (connection); +} + +static void +shared_connections_shutdown (void *data) +{ + int n_entries; + + _DBUS_LOCK (shared_connections); + + /* This is a little bit unpleasant... better ideas? */ + while ((n_entries = _dbus_hash_table_get_n_entries (shared_connections)) > 0) + { + DBusConnection *connection; + DBusHashIter iter; + + _dbus_hash_iter_init (shared_connections, &iter); + _dbus_hash_iter_next (&iter); + + connection = _dbus_hash_iter_get_value (&iter); + + _DBUS_UNLOCK (shared_connections); + close_connection_on_shutdown (connection); + _DBUS_LOCK (shared_connections); + + /* The connection should now be dead and not in our hash ... */ + _dbus_assert (_dbus_hash_table_get_n_entries (shared_connections) < n_entries); + } + + _dbus_assert (_dbus_hash_table_get_n_entries (shared_connections) == 0); + + _dbus_hash_table_unref (shared_connections); + shared_connections = NULL; + + if (shared_connections_no_guid != NULL) + { + DBusConnection *connection; + connection = _dbus_list_pop_first (&shared_connections_no_guid); + while (connection != NULL) + { + _DBUS_UNLOCK (shared_connections); + close_connection_on_shutdown (connection); + _DBUS_LOCK (shared_connections); + connection = _dbus_list_pop_first (&shared_connections_no_guid); + } + } + + shared_connections_no_guid = NULL; + + _DBUS_UNLOCK (shared_connections); +} + +static dbus_bool_t +connection_lookup_shared (DBusAddressEntry *entry, + DBusConnection **result) +{ + _dbus_verbose ("checking for existing connection\n"); + + *result = NULL; + + _DBUS_LOCK (shared_connections); + + if (shared_connections == NULL) + { + _dbus_verbose ("creating shared_connections hash table\n"); + + shared_connections = _dbus_hash_table_new (DBUS_HASH_STRING, + dbus_free, + NULL); + if (shared_connections == NULL) + { + _DBUS_UNLOCK (shared_connections); + return FALSE; + } + + if (!_dbus_register_shutdown_func (shared_connections_shutdown, NULL)) + { + _dbus_hash_table_unref (shared_connections); + shared_connections = NULL; + _DBUS_UNLOCK (shared_connections); + return FALSE; + } + + _dbus_verbose (" successfully created shared_connections\n"); + + _DBUS_UNLOCK (shared_connections); + return TRUE; /* no point looking up in the hash we just made */ + } + else + { + const char *guid; + + guid = dbus_address_entry_get_value (entry, "guid"); + + if (guid != NULL) + { + DBusConnection *connection; + + connection = _dbus_hash_table_lookup_string (shared_connections, + guid); + + if (connection) + { + /* The DBusConnection can't be finalized without taking + * the shared_connections lock to remove it from the + * hash. So it's safe to ref the connection here. + * However, it may be disconnected if the Disconnected + * message hasn't been processed yet, in which case we + * want to pretend it isn't in the hash and avoid + * returning it. + * + * The idea is to avoid ever returning a disconnected connection + * from dbus_connection_open(). We could just synchronously + * drop our shared ref to the connection on connection disconnect, + * and then assert here that the connection is connected, but + * that causes reentrancy headaches. + */ + CONNECTION_LOCK (connection); + if (_dbus_connection_get_is_connected_unlocked (connection)) + { + _dbus_connection_ref_unlocked (connection); + *result = connection; + _dbus_verbose ("looked up existing connection to server guid %s\n", + guid); + } + else + { + _dbus_verbose ("looked up existing connection to server guid %s but it was disconnected so ignoring it\n", + guid); + } + CONNECTION_UNLOCK (connection); + } + } + + _DBUS_UNLOCK (shared_connections); + return TRUE; + } +} + +static dbus_bool_t +connection_record_shared_unlocked (DBusConnection *connection, + const char *guid) +{ + char *guid_key; + char *guid_in_connection; + + HAVE_LOCK_CHECK (connection); + _dbus_assert (connection->server_guid == NULL); + _dbus_assert (connection->shareable); + + /* get a hard ref on this connection, even if + * we won't in fact store it in the hash, we still + * need to hold a ref on it until it's disconnected. + */ + _dbus_connection_ref_unlocked (connection); + + if (guid == NULL) + { + _DBUS_LOCK (shared_connections); + + if (!_dbus_list_prepend (&shared_connections_no_guid, connection)) + { + _DBUS_UNLOCK (shared_connections); + return FALSE; + } + + _DBUS_UNLOCK (shared_connections); + return TRUE; /* don't store in the hash */ + } + + /* A separate copy of the key is required in the hash table, because + * we don't have a lock on the connection when we are doing a hash + * lookup. + */ + + guid_key = _dbus_strdup (guid); + if (guid_key == NULL) + return FALSE; + + guid_in_connection = _dbus_strdup (guid); + if (guid_in_connection == NULL) + { + dbus_free (guid_key); + return FALSE; + } + + _DBUS_LOCK (shared_connections); + _dbus_assert (shared_connections != NULL); + + if (!_dbus_hash_table_insert_string (shared_connections, + guid_key, connection)) + { + dbus_free (guid_key); + dbus_free (guid_in_connection); + _DBUS_UNLOCK (shared_connections); + return FALSE; + } + + connection->server_guid = guid_in_connection; + + _dbus_verbose ("stored connection to %s to be shared\n", + connection->server_guid); + + _DBUS_UNLOCK (shared_connections); + + _dbus_assert (connection->server_guid != NULL); + + return TRUE; +} + +static void +connection_forget_shared_unlocked (DBusConnection *connection) +{ + HAVE_LOCK_CHECK (connection); + + if (!connection->shareable) + return; + + _DBUS_LOCK (shared_connections); + + if (connection->server_guid != NULL) + { + _dbus_verbose ("dropping connection to %s out of the shared table\n", + connection->server_guid); + + if (!_dbus_hash_table_remove_string (shared_connections, + connection->server_guid)) + _dbus_assert_not_reached ("connection was not in the shared table"); + + dbus_free (connection->server_guid); + connection->server_guid = NULL; + } + else + { + _dbus_list_remove (&shared_connections_no_guid, connection); + } + + _DBUS_UNLOCK (shared_connections); + + /* remove our reference held on all shareable connections */ + _dbus_connection_unref_unlocked (connection); +} + +static DBusConnection* +connection_try_from_address_entry (DBusAddressEntry *entry, + DBusError *error) +{ + DBusTransport *transport; + DBusConnection *connection; + + transport = _dbus_transport_open (entry, error); + + if (transport == NULL) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + return NULL; + } + + connection = _dbus_connection_new_for_transport (transport); + + _dbus_transport_unref (transport); + + if (connection == NULL) + { + _DBUS_SET_OOM (error); + return NULL; + } + +#ifndef DBUS_DISABLE_CHECKS + _dbus_assert (!connection->have_connection_lock); +#endif + return connection; +} + +/* + * If the shared parameter is true, then any existing connection will + * be used (and if a new connection is created, it will be available + * for use by others). If the shared parameter is false, a new + * connection will always be created, and the new connection will + * never be returned to other callers. + * + * @param address the address + * @param shared whether the connection is shared or private + * @param error error return + * @returns the connection or #NULL on error + */ +static DBusConnection* +_dbus_connection_open_internal (const char *address, + dbus_bool_t shared, + DBusError *error) +{ + DBusConnection *connection; + DBusAddressEntry **entries; + DBusError tmp_error = DBUS_ERROR_INIT; + DBusError first_error = DBUS_ERROR_INIT; + int len, i; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + _dbus_verbose ("opening %s connection to: %s\n", + shared ? "shared" : "private", address); + + if (!dbus_parse_address (address, &entries, &len, error)) + return NULL; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + connection = NULL; + + for (i = 0; i < len; i++) + { + if (shared) + { + if (!connection_lookup_shared (entries[i], &connection)) + _DBUS_SET_OOM (&tmp_error); + } + + if (connection == NULL) + { + connection = connection_try_from_address_entry (entries[i], + &tmp_error); + + if (connection != NULL && shared) + { + const char *guid; + + connection->shareable = TRUE; + + /* guid may be NULL */ + guid = dbus_address_entry_get_value (entries[i], "guid"); + + CONNECTION_LOCK (connection); + + if (!connection_record_shared_unlocked (connection, guid)) + { + _DBUS_SET_OOM (&tmp_error); + _dbus_connection_close_possibly_shared_and_unlock (connection); + dbus_connection_unref (connection); + connection = NULL; + } + else + CONNECTION_UNLOCK (connection); + } + } + + if (connection) + break; + + _DBUS_ASSERT_ERROR_IS_SET (&tmp_error); + + if (i == 0) + dbus_move_error (&tmp_error, &first_error); + else + dbus_error_free (&tmp_error); + } + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error); + + if (connection == NULL) + { + _DBUS_ASSERT_ERROR_IS_SET (&first_error); + dbus_move_error (&first_error, error); + } + else + dbus_error_free (&first_error); + + dbus_address_entries_free (entries); + return connection; +} + +/** + * Closes a shared OR private connection, while dbus_connection_close() can + * only be used on private connections. Should only be called by the + * dbus code that owns the connection - an owner must be known, + * the open/close state is like malloc/free, not like ref/unref. + * + * @param connection the connection + */ +void +_dbus_connection_close_possibly_shared (DBusConnection *connection) +{ + _dbus_assert (connection != NULL); + _dbus_assert (connection->generation == _dbus_current_generation); + + CONNECTION_LOCK (connection); + _dbus_connection_close_possibly_shared_and_unlock (connection); +} + +static DBusPreallocatedSend* +_dbus_connection_preallocate_send_unlocked (DBusConnection *connection) +{ + DBusPreallocatedSend *preallocated; + + HAVE_LOCK_CHECK (connection); + + _dbus_assert (connection != NULL); + + preallocated = dbus_new (DBusPreallocatedSend, 1); + if (preallocated == NULL) + return NULL; + + if (connection->link_cache != NULL) + { + preallocated->queue_link = + _dbus_list_pop_first_link (&connection->link_cache); + preallocated->queue_link->data = NULL; + } + else + { + preallocated->queue_link = _dbus_list_alloc_link (NULL); + if (preallocated->queue_link == NULL) + goto failed_0; + } + + if (connection->link_cache != NULL) + { + preallocated->counter_link = + _dbus_list_pop_first_link (&connection->link_cache); + preallocated->counter_link->data = connection->outgoing_counter; + } + else + { + preallocated->counter_link = _dbus_list_alloc_link (connection->outgoing_counter); + if (preallocated->counter_link == NULL) + goto failed_1; + } + + _dbus_counter_ref (preallocated->counter_link->data); + + preallocated->connection = connection; + + return preallocated; + + failed_1: + _dbus_list_free_link (preallocated->queue_link); + failed_0: + dbus_free (preallocated); + + return NULL; +} + +/* Called with lock held, does not update dispatch status */ +static void +_dbus_connection_send_preallocated_unlocked_no_update (DBusConnection *connection, + DBusPreallocatedSend *preallocated, + DBusMessage *message, + dbus_uint32_t *client_serial) +{ + dbus_uint32_t serial; + const char *sig; + + preallocated->queue_link->data = message; + _dbus_list_prepend_link (&connection->outgoing_messages, + preallocated->queue_link); + + _dbus_message_add_size_counter_link (message, + preallocated->counter_link); + + dbus_free (preallocated); + preallocated = NULL; + + dbus_message_ref (message); + + connection->n_outgoing += 1; + + sig = dbus_message_get_signature (message); + + _dbus_verbose ("Message %p (%d %s %s %s '%s') for %s added to outgoing queue %p, %d pending to send\n", + message, + dbus_message_get_type (message), + dbus_message_get_path (message) ? + dbus_message_get_path (message) : + "no path", + dbus_message_get_interface (message) ? + dbus_message_get_interface (message) : + "no interface", + dbus_message_get_member (message) ? + dbus_message_get_member (message) : + "no member", + sig, + dbus_message_get_destination (message) ? + dbus_message_get_destination (message) : + "null", + connection, + connection->n_outgoing); + + if (dbus_message_get_serial (message) == 0) + { + serial = _dbus_connection_get_next_client_serial (connection); + dbus_message_set_serial (message, serial); + if (client_serial) + *client_serial = serial; + } + else + { + if (client_serial) + *client_serial = dbus_message_get_serial (message); + } + + _dbus_verbose ("Message %p serial is %u\n", + message, dbus_message_get_serial (message)); + + dbus_message_lock (message); + + /* Now we need to run an iteration to hopefully just write the messages + * out immediately, and otherwise get them queued up + */ + _dbus_connection_do_iteration_unlocked (connection, + DBUS_ITERATION_DO_WRITING, + -1); + + /* If stuff is still queued up, be sure we wake up the main loop */ + if (connection->n_outgoing > 0) + _dbus_connection_wakeup_mainloop (connection); +} + +static void +_dbus_connection_send_preallocated_and_unlock (DBusConnection *connection, + DBusPreallocatedSend *preallocated, + DBusMessage *message, + dbus_uint32_t *client_serial) +{ + DBusDispatchStatus status; + + HAVE_LOCK_CHECK (connection); + + _dbus_connection_send_preallocated_unlocked_no_update (connection, + preallocated, + message, client_serial); + + _dbus_verbose ("%s middle\n", _DBUS_FUNCTION_NAME); + status = _dbus_connection_get_dispatch_status_unlocked (connection); + + /* this calls out to user code */ + _dbus_connection_update_dispatch_status_and_unlock (connection, status); +} + +/** + * Like dbus_connection_send(), but assumes the connection + * is already locked on function entry, and unlocks before returning. + * + * @param connection the connection + * @param message the message to send + * @param client_serial return location for client serial of sent message + * @returns #FALSE on out-of-memory + */ +dbus_bool_t +_dbus_connection_send_and_unlock (DBusConnection *connection, + DBusMessage *message, + dbus_uint32_t *client_serial) +{ + DBusPreallocatedSend *preallocated; + + _dbus_assert (connection != NULL); + _dbus_assert (message != NULL); + + preallocated = _dbus_connection_preallocate_send_unlocked (connection); + if (preallocated == NULL) + { + CONNECTION_UNLOCK (connection); + return FALSE; + } + + _dbus_connection_send_preallocated_and_unlock (connection, + preallocated, + message, + client_serial); + return TRUE; +} + +/** + * Used internally to handle the semantics of dbus_server_set_new_connection_function(). + * If the new connection function does not ref the connection, we want to close it. + * + * A bit of a hack, probably the new connection function should have returned a value + * for whether to close, or should have had to close the connection itself if it + * didn't want it. + * + * But, this works OK as long as the new connection function doesn't do anything + * crazy like keep the connection around without ref'ing it. + * + * We have to lock the connection across refcount check and close in case + * the new connection function spawns a thread that closes and unrefs. + * In that case, if the app thread + * closes and unrefs first, we'll harmlessly close again; if the app thread + * still has the ref, we'll close and then the app will close harmlessly. + * If the app unrefs without closing, the app is broken since if the + * app refs from the new connection function it is supposed to also close. + * + * If we didn't atomically check the refcount and close with the lock held + * though, we could screw this up. + * + * @param connection the connection + */ +void +_dbus_connection_close_if_only_one_ref (DBusConnection *connection) +{ + CONNECTION_LOCK (connection); + + _dbus_assert (connection->refcount.value > 0); + + if (connection->refcount.value == 1) + _dbus_connection_close_possibly_shared_and_unlock (connection); + else + CONNECTION_UNLOCK (connection); +} + + +/** + * When a function that blocks has been called with a timeout, and we + * run out of memory, the time to wait for memory is based on the + * timeout. If the caller was willing to block a long time we wait a + * relatively long time for memory, if they were only willing to block + * briefly then we retry for memory at a rapid rate. + * + * @timeout_milliseconds the timeout requested for blocking + */ +static void +_dbus_memory_pause_based_on_timeout (int timeout_milliseconds) +{ + if (timeout_milliseconds == -1) + _dbus_sleep_milliseconds (1000); + else if (timeout_milliseconds < 100) + ; /* just busy loop */ + else if (timeout_milliseconds <= 1000) + _dbus_sleep_milliseconds (timeout_milliseconds / 3); + else + _dbus_sleep_milliseconds (1000); +} + +static DBusMessage * +generate_local_error_message (dbus_uint32_t serial, + char *error_name, + char *error_msg) +{ + DBusMessage *message; + message = dbus_message_new (DBUS_MESSAGE_TYPE_ERROR); + if (!message) + goto out; + + if (!dbus_message_set_error_name (message, error_name)) + { + dbus_message_unref (message); + message = NULL; + goto out; + } + + dbus_message_set_no_reply (message, TRUE); + + if (!dbus_message_set_reply_serial (message, + serial)) + { + dbus_message_unref (message); + message = NULL; + goto out; + } + + if (error_msg != NULL) + { + DBusMessageIter iter; + + dbus_message_iter_init_append (message, &iter); + if (!dbus_message_iter_append_basic (&iter, + DBUS_TYPE_STRING, + &error_msg)) + { + dbus_message_unref (message); + message = NULL; + goto out; + } + } + + out: + return message; +} + + +/* This is slightly strange since we can pop a message here without + * the dispatch lock. + */ +static DBusMessage* +check_for_reply_unlocked (DBusConnection *connection, + dbus_uint32_t client_serial) +{ + DBusList *link; + + HAVE_LOCK_CHECK (connection); + + link = _dbus_list_get_first_link (&connection->incoming_messages); + + while (link != NULL) + { + DBusMessage *reply = link->data; + + if (dbus_message_get_reply_serial (reply) == client_serial) + { + _dbus_list_remove_link (&connection->incoming_messages, link); + connection->n_incoming -= 1; + return reply; + } + link = _dbus_list_get_next_link (&connection->incoming_messages, link); + } + + return NULL; +} + +static void +connection_timeout_and_complete_all_pending_calls_unlocked (DBusConnection *connection) +{ + /* We can't iterate over the hash in the normal way since we'll be + * dropping the lock for each item. So we restart the + * iter each time as we drain the hash table. + */ + + while (_dbus_hash_table_get_n_entries (connection->pending_replies) > 0) + { + DBusPendingCall *pending; + DBusHashIter iter; + + _dbus_hash_iter_init (connection->pending_replies, &iter); + _dbus_hash_iter_next (&iter); + + pending = _dbus_hash_iter_get_value (&iter); + _dbus_pending_call_ref_unlocked (pending); + + _dbus_pending_call_queue_timeout_error_unlocked (pending, + connection); + _dbus_connection_remove_timeout_unlocked (connection, + _dbus_pending_call_get_timeout_unlocked (pending)); + _dbus_pending_call_set_timeout_added_unlocked (pending, FALSE); + _dbus_hash_iter_remove_entry (&iter); + + _dbus_pending_call_unref_and_unlock (pending); + CONNECTION_LOCK (connection); + } + HAVE_LOCK_CHECK (connection); +} + +static void +complete_pending_call_and_unlock (DBusConnection *connection, + DBusPendingCall *pending, + DBusMessage *message) +{ + _dbus_pending_call_set_reply_unlocked (pending, message); + _dbus_pending_call_ref_unlocked (pending); /* in case there's no app with a ref held */ + _dbus_connection_detach_pending_call_and_unlock (connection, pending); + + /* Must be called unlocked since it invokes app callback */ + _dbus_pending_call_complete (pending); + dbus_pending_call_unref (pending); +} + +static dbus_bool_t +check_for_reply_and_update_dispatch_unlocked (DBusConnection *connection, + DBusPendingCall *pending) +{ + DBusMessage *reply; + DBusDispatchStatus status; + + reply = check_for_reply_unlocked (connection, + _dbus_pending_call_get_reply_serial_unlocked (pending)); + if (reply != NULL) + { + _dbus_verbose ("%s checked for reply\n", _DBUS_FUNCTION_NAME); + + _dbus_verbose ("dbus_connection_send_with_reply_and_block(): got reply\n"); + + complete_pending_call_and_unlock (connection, pending, reply); + dbus_message_unref (reply); + + CONNECTION_LOCK (connection); + status = _dbus_connection_get_dispatch_status_unlocked (connection); + _dbus_connection_update_dispatch_status_and_unlock (connection, status); + dbus_pending_call_unref (pending); + + return TRUE; + } + + return FALSE; +} + +/** + * Blocks until a pending call times out or gets a reply. + * + * Does not re-enter the main loop or run filter/path-registered + * callbacks. The reply to the message will not be seen by + * filter callbacks. + * + * Returns immediately if pending call already got a reply. + * + * @todo could use performance improvements (it keeps scanning + * the whole message queue for example) + * + * @param pending the pending call we block for a reply on + */ +void +_dbus_connection_block_pending_call (DBusPendingCall *pending) +{ + long start_tv_sec, start_tv_usec; + long end_tv_sec, end_tv_usec; + long tv_sec, tv_usec; + DBusDispatchStatus status; + DBusConnection *connection; + dbus_uint32_t client_serial; + int timeout_milliseconds; + + _dbus_assert (pending != NULL); + + if (dbus_pending_call_get_completed (pending)) + return; + + dbus_pending_call_ref (pending); /* necessary because the call could be canceled */ + + connection = _dbus_pending_call_get_connection_and_lock (pending); + + /* Flush message queue - note, can affect dispatch status */ + _dbus_connection_flush_unlocked (connection); + + client_serial = _dbus_pending_call_get_reply_serial_unlocked (pending); + + /* note that timeout_milliseconds is limited to a smallish value + * in _dbus_pending_call_new() so overflows aren't possible + * below + */ + timeout_milliseconds = dbus_timeout_get_interval (_dbus_pending_call_get_timeout_unlocked (pending)); + + _dbus_get_current_time (&start_tv_sec, &start_tv_usec); + end_tv_sec = start_tv_sec + timeout_milliseconds / 1000; + end_tv_usec = start_tv_usec + (timeout_milliseconds % 1000) * 1000; + end_tv_sec += end_tv_usec / _DBUS_USEC_PER_SECOND; + end_tv_usec = end_tv_usec % _DBUS_USEC_PER_SECOND; + + _dbus_verbose ("dbus_connection_send_with_reply_and_block(): will block %d milliseconds for reply serial %u from %ld sec %ld usec to %ld sec %ld usec\n", + timeout_milliseconds, + client_serial, + start_tv_sec, start_tv_usec, + end_tv_sec, end_tv_usec); + + /* check to see if we already got the data off the socket */ + /* from another blocked pending call */ + if (check_for_reply_and_update_dispatch_unlocked (connection, pending)) + return; + + /* Now we wait... */ + /* always block at least once as we know we don't have the reply yet */ + _dbus_connection_do_iteration_unlocked (connection, + DBUS_ITERATION_DO_READING | + DBUS_ITERATION_BLOCK, + timeout_milliseconds); + + recheck_status: + + _dbus_verbose ("%s top of recheck\n", _DBUS_FUNCTION_NAME); + + HAVE_LOCK_CHECK (connection); + + /* queue messages and get status */ + + status = _dbus_connection_get_dispatch_status_unlocked (connection); + + /* the get_completed() is in case a dispatch() while we were blocking + * got the reply instead of us. + */ + if (_dbus_pending_call_get_completed_unlocked (pending)) + { + _dbus_verbose ("Pending call completed by dispatch in %s\n", _DBUS_FUNCTION_NAME); + _dbus_connection_update_dispatch_status_and_unlock (connection, status); + dbus_pending_call_unref (pending); + return; + } + + if (status == DBUS_DISPATCH_DATA_REMAINS) + { + if (check_for_reply_and_update_dispatch_unlocked (connection, pending)) + return; + } + + _dbus_get_current_time (&tv_sec, &tv_usec); + + if (!_dbus_connection_get_is_connected_unlocked (connection)) + { + DBusMessage *error_msg; + + error_msg = generate_local_error_message (client_serial, + DBUS_ERROR_DISCONNECTED, + "Connection was disconnected before a reply was received"); + + /* on OOM error_msg is set to NULL */ + complete_pending_call_and_unlock (connection, pending, error_msg); + dbus_pending_call_unref (pending); + return; + } + else if (tv_sec < start_tv_sec) + _dbus_verbose ("dbus_connection_send_with_reply_and_block(): clock set backward\n"); + else if (connection->disconnect_message_link == NULL) + _dbus_verbose ("dbus_connection_send_with_reply_and_block(): disconnected\n"); + else if (tv_sec < end_tv_sec || + (tv_sec == end_tv_sec && tv_usec < end_tv_usec)) + { + timeout_milliseconds = (end_tv_sec - tv_sec) * 1000 + + (end_tv_usec - tv_usec) / 1000; + _dbus_verbose ("dbus_connection_send_with_reply_and_block(): %d milliseconds remain\n", timeout_milliseconds); + _dbus_assert (timeout_milliseconds >= 0); + + if (status == DBUS_DISPATCH_NEED_MEMORY) + { + /* Try sleeping a bit, as we aren't sure we need to block for reading, + * we may already have a reply in the buffer and just can't process + * it. + */ + _dbus_verbose ("dbus_connection_send_with_reply_and_block() waiting for more memory\n"); + + _dbus_memory_pause_based_on_timeout (timeout_milliseconds); + } + else + { + /* block again, we don't have the reply buffered yet. */ + _dbus_connection_do_iteration_unlocked (connection, + DBUS_ITERATION_DO_READING | + DBUS_ITERATION_BLOCK, + timeout_milliseconds); + } + + goto recheck_status; + } + + _dbus_verbose ("dbus_connection_send_with_reply_and_block(): Waited %ld milliseconds and got no reply\n", + (tv_sec - start_tv_sec) * 1000 + (tv_usec - start_tv_usec) / 1000); + + _dbus_assert (!_dbus_pending_call_get_completed_unlocked (pending)); + + /* unlock and call user code */ + complete_pending_call_and_unlock (connection, pending, NULL); + + /* update user code on dispatch status */ + CONNECTION_LOCK (connection); + status = _dbus_connection_get_dispatch_status_unlocked (connection); + _dbus_connection_update_dispatch_status_and_unlock (connection, status); + dbus_pending_call_unref (pending); +} + +/** @} */ + +/** + * @addtogroup DBusConnection + * + * @{ + */ + +/** + * Gets a connection to a remote address. If a connection to the given + * address already exists, returns the existing connection with its + * reference count incremented. Otherwise, returns a new connection + * and saves the new connection for possible re-use if a future call + * to dbus_connection_open() asks to connect to the same server. + * + * Use dbus_connection_open_private() to get a dedicated connection + * not shared with other callers of dbus_connection_open(). + * + * If the open fails, the function returns #NULL, and provides a + * reason for the failure in the error parameter. Pass #NULL for the + * error parameter if you aren't interested in the reason for + * failure. + * + * Because this connection is shared, no user of the connection + * may call dbus_connection_close(). However, when you are done with the + * connection you should call dbus_connection_unref(). + * + * @note Prefer dbus_connection_open() to dbus_connection_open_private() + * unless you have good reason; connections are expensive enough + * that it's wasteful to create lots of connections to the same + * server. + * + * @param address the address. + * @param error address where an error can be returned. + * @returns new connection, or #NULL on failure. + */ +DBusConnection* +dbus_connection_open (const char *address, + DBusError *error) +{ + DBusConnection *connection; + + _dbus_return_val_if_fail (address != NULL, NULL); + _dbus_return_val_if_error_is_set (error, NULL); + + connection = _dbus_connection_open_internal (address, + TRUE, + error); + + return connection; +} + +/** + * Opens a new, dedicated connection to a remote address. Unlike + * dbus_connection_open(), always creates a new connection. + * This connection will not be saved or recycled by libdbus. + * + * If the open fails, the function returns #NULL, and provides a + * reason for the failure in the error parameter. Pass #NULL for the + * error parameter if you aren't interested in the reason for + * failure. + * + * When you are done with this connection, you must + * dbus_connection_close() to disconnect it, + * and dbus_connection_unref() to free the connection object. + * + * (The dbus_connection_close() can be skipped if the + * connection is already known to be disconnected, for example + * if you are inside a handler for the Disconnected signal.) + * + * @note Prefer dbus_connection_open() to dbus_connection_open_private() + * unless you have good reason; connections are expensive enough + * that it's wasteful to create lots of connections to the same + * server. + * + * @param address the address. + * @param error address where an error can be returned. + * @returns new connection, or #NULL on failure. + */ +DBusConnection* +dbus_connection_open_private (const char *address, + DBusError *error) +{ + DBusConnection *connection; + + _dbus_return_val_if_fail (address != NULL, NULL); + _dbus_return_val_if_error_is_set (error, NULL); + + connection = _dbus_connection_open_internal (address, + FALSE, + error); + + return connection; +} + +/** + * Increments the reference count of a DBusConnection. + * + * @param connection the connection. + * @returns the connection. + */ +DBusConnection * +dbus_connection_ref (DBusConnection *connection) +{ + _dbus_return_val_if_fail (connection != NULL, NULL); + _dbus_return_val_if_fail (connection->generation == _dbus_current_generation, NULL); + + /* The connection lock is better than the global + * lock in the atomic increment fallback + */ + +#ifdef DBUS_HAVE_ATOMIC_INT + _dbus_atomic_inc (&connection->refcount); +#else + CONNECTION_LOCK (connection); + _dbus_assert (connection->refcount.value > 0); + + connection->refcount.value += 1; + CONNECTION_UNLOCK (connection); +#endif + + return connection; +} + +static void +free_outgoing_message (void *element, + void *data) +{ + DBusMessage *message = element; + DBusConnection *connection = data; + + _dbus_message_remove_size_counter (message, + connection->outgoing_counter, + NULL); + dbus_message_unref (message); +} + +/* This is run without the mutex held, but after the last reference + * to the connection has been dropped we should have no thread-related + * problems + */ +static void +_dbus_connection_last_unref (DBusConnection *connection) +{ + DBusList *link; + + _dbus_verbose ("Finalizing connection %p\n", connection); + + _dbus_assert (connection->refcount.value == 0); + + /* You have to disconnect the connection before unref:ing it. Otherwise + * you won't get the disconnected message. + */ + _dbus_assert (!_dbus_transport_get_is_connected (connection->transport)); + _dbus_assert (connection->server_guid == NULL); + + /* ---- We're going to call various application callbacks here, hope it doesn't break anything... */ + _dbus_object_tree_free_all_unlocked (connection->objects); + + dbus_connection_set_dispatch_status_function (connection, NULL, NULL, NULL); + dbus_connection_set_wakeup_main_function (connection, NULL, NULL, NULL); + dbus_connection_set_unix_user_function (connection, NULL, NULL, NULL); + + _dbus_watch_list_free (connection->watches); + connection->watches = NULL; + + _dbus_timeout_list_free (connection->timeouts); + connection->timeouts = NULL; + + _dbus_data_slot_list_free (&connection->slot_list); + + link = _dbus_list_get_first_link (&connection->filter_list); + while (link != NULL) + { + DBusMessageFilter *filter = link->data; + DBusList *next = _dbus_list_get_next_link (&connection->filter_list, link); + + filter->function = NULL; + _dbus_message_filter_unref (filter); /* calls app callback */ + link->data = NULL; + + link = next; + } + _dbus_list_clear (&connection->filter_list); + + /* ---- Done with stuff that invokes application callbacks */ + + _dbus_object_tree_unref (connection->objects); + + _dbus_hash_table_unref (connection->pending_replies); + connection->pending_replies = NULL; + + _dbus_list_clear (&connection->filter_list); + + _dbus_list_foreach (&connection->outgoing_messages, + free_outgoing_message, + connection); + _dbus_list_clear (&connection->outgoing_messages); + + _dbus_list_foreach (&connection->incoming_messages, + (DBusForeachFunction) dbus_message_unref, + NULL); + _dbus_list_clear (&connection->incoming_messages); + + _dbus_counter_unref (connection->outgoing_counter); + + _dbus_transport_unref (connection->transport); + + if (connection->disconnect_message_link) + { + DBusMessage *message = connection->disconnect_message_link->data; + dbus_message_unref (message); + _dbus_list_free_link (connection->disconnect_message_link); + } + + _dbus_list_clear (&connection->link_cache); + + _dbus_condvar_free_at_location (&connection->dispatch_cond); + _dbus_condvar_free_at_location (&connection->io_path_cond); + + _dbus_mutex_free_at_location (&connection->io_path_mutex); + _dbus_mutex_free_at_location (&connection->dispatch_mutex); + + _dbus_mutex_free_at_location (&connection->mutex); + + dbus_free (connection); +} + +/** + * Decrements the reference count of a DBusConnection, and finalizes + * it if the count reaches zero. + * + * Note: it is a bug to drop the last reference to a connection that + * is still connected. + * + * For shared connections, libdbus will own a reference + * as long as the connection is connected, so you can know that either + * you don't have the last reference, or it's OK to drop the last reference. + * Most connections are shared. dbus_connection_open() and dbus_bus_get() + * return shared connections. + * + * For private connections, the creator of the connection must arrange for + * dbus_connection_close() to be called prior to dropping the last reference. + * Private connections come from dbus_connection_open_private() or dbus_bus_get_private(). + * + * @param connection the connection. + */ +void +dbus_connection_unref (DBusConnection *connection) +{ + dbus_bool_t last_unref; + + _dbus_return_if_fail (connection != NULL); + _dbus_return_if_fail (connection->generation == _dbus_current_generation); + + /* The connection lock is better than the global + * lock in the atomic increment fallback + */ + +#ifdef DBUS_HAVE_ATOMIC_INT + last_unref = (_dbus_atomic_dec (&connection->refcount) == 1); +#else + CONNECTION_LOCK (connection); + + _dbus_assert (connection->refcount.value > 0); + + connection->refcount.value -= 1; + last_unref = (connection->refcount.value == 0); + +#if 0 + printf ("unref() connection %p count = %d\n", connection, connection->refcount.value); +#endif + + CONNECTION_UNLOCK (connection); +#endif + + if (last_unref) + { +#ifndef DBUS_DISABLE_CHECKS + if (_dbus_transport_get_is_connected (connection->transport)) + { + _dbus_warn_check_failed ("The last reference on a connection was dropped without closing the connection. This is a bug in an application. See dbus_connection_unref() documentation for details.\n%s", + connection->shareable ? + "Most likely, the application called unref() too many times and removed a reference belonging to libdbus, since this is a shared connection.\n" : + "Most likely, the application was supposed to call dbus_connection_close(), since this is a private connection.\n"); + return; + } +#endif + _dbus_connection_last_unref (connection); + } +} + +/* + * Note that the transport can disconnect itself (other end drops us) + * and in that case this function never runs. So this function must + * not do anything more than disconnect the transport and update the + * dispatch status. + * + * If the transport self-disconnects, then we assume someone will + * dispatch the connection to cause the dispatch status update. + */ +static void +_dbus_connection_close_possibly_shared_and_unlock (DBusConnection *connection) +{ + DBusDispatchStatus status; + + HAVE_LOCK_CHECK (connection); + + _dbus_verbose ("Disconnecting %p\n", connection); + + /* We need to ref because update_dispatch_status_and_unlock will unref + * the connection if it was shared and libdbus was the only remaining + * refcount holder. + */ + _dbus_connection_ref_unlocked (connection); + + _dbus_transport_disconnect (connection->transport); + + /* This has the side effect of queuing the disconnect message link + * (unless we don't have enough memory, possibly, so don't assert it). + * After the disconnect message link is queued, dbus_bus_get/dbus_connection_open + * should never again return the newly-disconnected connection. + * + * However, we only unref the shared connection and exit_on_disconnect when + * the disconnect message reaches the head of the message queue, + * NOT when it's first queued. + */ + status = _dbus_connection_get_dispatch_status_unlocked (connection); + + /* This calls out to user code */ + _dbus_connection_update_dispatch_status_and_unlock (connection, status); + + /* Could also call out to user code */ + dbus_connection_unref (connection); +} + +/** + * Closes a private connection, so no further data can be sent or received. + * This disconnects the transport (such as a socket) underlying the + * connection. + * + * Attempts to send messages after closing a connection are safe, but will result in + * error replies generated locally in libdbus. + * + * This function does not affect the connection's reference count. It's + * safe to close a connection more than once; all calls after the + * first do nothing. It's impossible to "reopen" a connection, a + * new connection must be created. This function may result in a call + * to the DBusDispatchStatusFunction set with + * dbus_connection_set_dispatch_status_function(), as the disconnect + * message it generates needs to be dispatched. + * + * If a connection is dropped by the remote application, it will + * close itself. + * + * You must close a connection prior to releasing the last reference to + * the connection. If you dbus_connection_unref() for the last time + * without closing the connection, the results are undefined; it + * is a bug in your program and libdbus will try to print a warning. + * + * You may not close a shared connection. Connections created with + * dbus_connection_open() or dbus_bus_get() are shared. + * These connections are owned by libdbus, and applications should + * only unref them, never close them. Applications can know it is + * safe to unref these connections because libdbus will be holding a + * reference as long as the connection is open. Thus, either the + * connection is closed and it is OK to drop the last reference, + * or the connection is open and the app knows it does not have the + * last reference. + * + * Connections created with dbus_connection_open_private() or + * dbus_bus_get_private() are not kept track of or referenced by + * libdbus. The creator of these connections is responsible for + * calling dbus_connection_close() prior to releasing the last + * reference, if the connection is not already disconnected. + * + * @param connection the private (unshared) connection to close + */ +void +dbus_connection_close (DBusConnection *connection) +{ + _dbus_return_if_fail (connection != NULL); + _dbus_return_if_fail (connection->generation == _dbus_current_generation); + + CONNECTION_LOCK (connection); + +#ifndef DBUS_DISABLE_CHECKS + if (connection->shareable) + { + CONNECTION_UNLOCK (connection); + + _dbus_warn_check_failed ("Applications must not close shared connections - see dbus_connection_close() docs. This is a bug in the application.\n"); + return; + } +#endif + + _dbus_connection_close_possibly_shared_and_unlock (connection); +} + +static dbus_bool_t +_dbus_connection_get_is_connected_unlocked (DBusConnection *connection) +{ + HAVE_LOCK_CHECK (connection); + return _dbus_transport_get_is_connected (connection->transport); +} + +/** + * Gets whether the connection is currently open. A connection may + * become disconnected when the remote application closes its end, or + * exits; a connection may also be disconnected with + * dbus_connection_close(). + * + * There are not separate states for "closed" and "disconnected," the two + * terms are synonymous. This function should really be called + * get_is_open() but for historical reasons is not. + * + * @param connection the connection. + * @returns #TRUE if the connection is still alive. + */ +dbus_bool_t +dbus_connection_get_is_connected (DBusConnection *connection) +{ + dbus_bool_t res; + + _dbus_return_val_if_fail (connection != NULL, FALSE); + + CONNECTION_LOCK (connection); + res = _dbus_connection_get_is_connected_unlocked (connection); + CONNECTION_UNLOCK (connection); + + return res; +} + +/** + * Gets whether the connection was authenticated. (Note that + * if the connection was authenticated then disconnected, + * this function still returns #TRUE) + * + * @param connection the connection + * @returns #TRUE if the connection was ever authenticated + */ +dbus_bool_t +dbus_connection_get_is_authenticated (DBusConnection *connection) +{ + dbus_bool_t res; + + _dbus_return_val_if_fail (connection != NULL, FALSE); + + CONNECTION_LOCK (connection); + res = _dbus_transport_get_is_authenticated (connection->transport); + CONNECTION_UNLOCK (connection); + + return res; +} + +/** + * Gets whether the connection is not authenticated as a specific + * user. If the connection is not authenticated, this function + * returns #TRUE, and if it is authenticated but as an anonymous user, + * it returns #TRUE. If it is authenticated as a specific user, then + * this returns #FALSE. (Note that if the connection was authenticated + * as anonymous then disconnected, this function still returns #TRUE.) + * + * If the connection is not anonymous, you can use + * dbus_connection_get_unix_user() and + * dbus_connection_get_windows_user() to see who it's authorized as. + * + * If you want to prevent non-anonymous authorization, use + * dbus_server_set_auth_mechanisms() to remove the mechanisms that + * allow proving user identity (i.e. only allow the ANONYMOUS + * mechanism). + * + * @param connection the connection + * @returns #TRUE if not authenticated or authenticated as anonymous + */ +dbus_bool_t +dbus_connection_get_is_anonymous (DBusConnection *connection) +{ + dbus_bool_t res; + + _dbus_return_val_if_fail (connection != NULL, FALSE); + + CONNECTION_LOCK (connection); + res = _dbus_transport_get_is_anonymous (connection->transport); + CONNECTION_UNLOCK (connection); + + return res; +} + +/** + * Gets the ID of the server address we are authenticated to, if this + * connection is on the client side. If the connection is on the + * server side, this will always return #NULL - use dbus_server_get_id() + * to get the ID of your own server, if you are the server side. + * + * If a client-side connection is not authenticated yet, the ID may be + * available if it was included in the server address, but may not be + * available. The only way to be sure the server ID is available + * is to wait for authentication to complete. + * + * In general, each mode of connecting to a given server will have + * its own ID. So for example, if the session bus daemon is listening + * on UNIX domain sockets and on TCP, then each of those modalities + * will have its own server ID. + * + * If you want an ID that identifies an entire session bus, look at + * dbus_bus_get_id() instead (which is just a convenience wrapper + * around the org.freedesktop.DBus.GetId method invoked on the bus). + * + * You can also get a machine ID; see dbus_get_local_machine_id() to + * get the machine you are on. There isn't a convenience wrapper, but + * you can invoke org.freedesktop.DBus.Peer.GetMachineId on any peer + * to get the machine ID on the other end. + * + * The D-Bus specification describes the server ID and other IDs in a + * bit more detail. + * + * @param connection the connection + * @returns the server ID or #NULL if no memory or the connection is server-side + */ +char* +dbus_connection_get_server_id (DBusConnection *connection) +{ + char *id; + + _dbus_return_val_if_fail (connection != NULL, NULL); + + CONNECTION_LOCK (connection); + id = _dbus_strdup (_dbus_transport_get_server_id (connection->transport)); + CONNECTION_UNLOCK (connection); + + return id; +} + +/** + * Set whether _exit() should be called when the connection receives a + * disconnect signal. The call to _exit() comes after any handlers for + * the disconnect signal run; handlers can cancel the exit by calling + * this function. + * + * By default, exit_on_disconnect is #FALSE; but for message bus + * connections returned from dbus_bus_get() it will be toggled on + * by default. + * + * @param connection the connection + * @param exit_on_disconnect #TRUE if _exit() should be called after a disconnect signal + */ +void +dbus_connection_set_exit_on_disconnect (DBusConnection *connection, + dbus_bool_t exit_on_disconnect) +{ + _dbus_return_if_fail (connection != NULL); + + CONNECTION_LOCK (connection); + connection->exit_on_disconnect = exit_on_disconnect != FALSE; + CONNECTION_UNLOCK (connection); +} + +/** + * Preallocates resources needed to send a message, allowing the message + * to be sent without the possibility of memory allocation failure. + * Allows apps to create a future guarantee that they can send + * a message regardless of memory shortages. + * + * @param connection the connection we're preallocating for. + * @returns the preallocated resources, or #NULL + */ +DBusPreallocatedSend* +dbus_connection_preallocate_send (DBusConnection *connection) +{ + DBusPreallocatedSend *preallocated; + + _dbus_return_val_if_fail (connection != NULL, NULL); + + CONNECTION_LOCK (connection); + + preallocated = + _dbus_connection_preallocate_send_unlocked (connection); + + CONNECTION_UNLOCK (connection); + + return preallocated; +} + +/** + * Frees preallocated message-sending resources from + * dbus_connection_preallocate_send(). Should only + * be called if the preallocated resources are not used + * to send a message. + * + * @param connection the connection + * @param preallocated the resources + */ +void +dbus_connection_free_preallocated_send (DBusConnection *connection, + DBusPreallocatedSend *preallocated) +{ + _dbus_return_if_fail (connection != NULL); + _dbus_return_if_fail (preallocated != NULL); + _dbus_return_if_fail (connection == preallocated->connection); + + _dbus_list_free_link (preallocated->queue_link); + _dbus_counter_unref (preallocated->counter_link->data); + _dbus_list_free_link (preallocated->counter_link); + dbus_free (preallocated); +} + +/** + * Sends a message using preallocated resources. This function cannot fail. + * It works identically to dbus_connection_send() in other respects. + * Preallocated resources comes from dbus_connection_preallocate_send(). + * This function "consumes" the preallocated resources, they need not + * be freed separately. + * + * @param connection the connection + * @param preallocated the preallocated resources + * @param message the message to send + * @param client_serial return location for client serial assigned to the message + */ +void +dbus_connection_send_preallocated (DBusConnection *connection, + DBusPreallocatedSend *preallocated, + DBusMessage *message, + dbus_uint32_t *client_serial) +{ + _dbus_return_if_fail (connection != NULL); + _dbus_return_if_fail (preallocated != NULL); + _dbus_return_if_fail (message != NULL); + _dbus_return_if_fail (preallocated->connection == connection); + _dbus_return_if_fail (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_CALL || + dbus_message_get_member (message) != NULL); + _dbus_return_if_fail (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_SIGNAL || + (dbus_message_get_interface (message) != NULL && + dbus_message_get_member (message) != NULL)); + + CONNECTION_LOCK (connection); + _dbus_connection_send_preallocated_and_unlock (connection, + preallocated, + message, client_serial); +} + +static dbus_bool_t +_dbus_connection_send_unlocked_no_update (DBusConnection *connection, + DBusMessage *message, + dbus_uint32_t *client_serial) +{ + DBusPreallocatedSend *preallocated; + + _dbus_assert (connection != NULL); + _dbus_assert (message != NULL); + + preallocated = _dbus_connection_preallocate_send_unlocked (connection); + if (preallocated == NULL) + return FALSE; + + _dbus_connection_send_preallocated_unlocked_no_update (connection, + preallocated, + message, + client_serial); + return TRUE; +} + +/** + * Adds a message to the outgoing message queue. Does not block to + * write the message to the network; that happens asynchronously. To + * force the message to be written, call dbus_connection_flush() however + * it is not necessary to call dbus_connection_flush() by hand; the + * message will be sent the next time the main loop is run. + * dbus_connection_flush() should only be used, for example, if + * the application was expected to exit before running the main loop. + * + * Because this only queues the message, the only reason it can + * fail is lack of memory. Even if the connection is disconnected, + * no error will be returned. If the function fails due to lack of memory, + * it returns #FALSE. The function will never fail for other reasons; even + * if the connection is disconnected, you can queue an outgoing message, + * though obviously it won't be sent. + * + * The message serial is used by the remote application to send a + * reply; see dbus_message_get_serial() or the D-Bus specification. + * + * dbus_message_unref() can be called as soon as this method returns + * as the message queue will hold its own ref until the message is sent. + * + * @param connection the connection. + * @param message the message to write. + * @param serial return location for message serial, or #NULL if you don't care + * @returns #TRUE on success. + */ +dbus_bool_t +dbus_connection_send (DBusConnection *connection, + DBusMessage *message, + dbus_uint32_t *serial) +{ + _dbus_return_val_if_fail (connection != NULL, FALSE); + _dbus_return_val_if_fail (message != NULL, FALSE); + + CONNECTION_LOCK (connection); + + return _dbus_connection_send_and_unlock (connection, + message, + serial); +} + +static dbus_bool_t +reply_handler_timeout (void *data) +{ + DBusConnection *connection; + DBusDispatchStatus status; + DBusPendingCall *pending = data; + + connection = _dbus_pending_call_get_connection_and_lock (pending); + + _dbus_pending_call_queue_timeout_error_unlocked (pending, + connection); + _dbus_connection_remove_timeout_unlocked (connection, + _dbus_pending_call_get_timeout_unlocked (pending)); + _dbus_pending_call_set_timeout_added_unlocked (pending, FALSE); + + _dbus_verbose ("%s middle\n", _DBUS_FUNCTION_NAME); + status = _dbus_connection_get_dispatch_status_unlocked (connection); + + /* Unlocks, and calls out to user code */ + _dbus_connection_update_dispatch_status_and_unlock (connection, status); + + return TRUE; +} + +/** + * Queues a message to send, as with dbus_connection_send(), + * but also returns a #DBusPendingCall used to receive a reply to the + * message. If no reply is received in the given timeout_milliseconds, + * this function expires the pending reply and generates a synthetic + * error reply (generated in-process, not by the remote application) + * indicating that a timeout occurred. + * + * A #DBusPendingCall will see a reply message before any filters or + * registered object path handlers. See dbus_connection_dispatch() for + * details on when handlers are run. + * + * A #DBusPendingCall will always see exactly one reply message, + * unless it's cancelled with dbus_pending_call_cancel(). + * + * If #NULL is passed for the pending_return, the #DBusPendingCall + * will still be generated internally, and used to track + * the message reply timeout. This means a timeout error will + * occur if no reply arrives, unlike with dbus_connection_send(). + * + * If -1 is passed for the timeout, a sane default timeout is used. -1 + * is typically the best value for the timeout for this reason, unless + * you want a very short or very long timeout. There is no way to + * avoid a timeout entirely, other than passing INT_MAX for the + * timeout to mean "very long timeout." libdbus clamps an INT_MAX + * timeout down to a few hours timeout though. + * + * @warning if the connection is disconnected, the #DBusPendingCall + * will be set to #NULL, so be careful with this. + * + * @param connection the connection + * @param message the message to send + * @param pending_return return location for a #DBusPendingCall object, or #NULL if connection is disconnected + * @param timeout_milliseconds timeout in milliseconds or -1 for default + * @returns #FALSE if no memory, #TRUE otherwise. + * + */ +dbus_bool_t +dbus_connection_send_with_reply (DBusConnection *connection, + DBusMessage *message, + DBusPendingCall **pending_return, + int timeout_milliseconds) +{ + DBusPendingCall *pending; + dbus_int32_t serial = -1; + DBusDispatchStatus status; + + _dbus_return_val_if_fail (connection != NULL, FALSE); + _dbus_return_val_if_fail (message != NULL, FALSE); + _dbus_return_val_if_fail (timeout_milliseconds >= 0 || timeout_milliseconds == -1, FALSE); + + if (pending_return) + *pending_return = NULL; + + CONNECTION_LOCK (connection); + + if (!_dbus_connection_get_is_connected_unlocked (connection)) + { + CONNECTION_UNLOCK (connection); + + return TRUE; + } + + pending = _dbus_pending_call_new_unlocked (connection, + timeout_milliseconds, + reply_handler_timeout); + + if (pending == NULL) + { + CONNECTION_UNLOCK (connection); + return FALSE; + } + + /* Assign a serial to the message */ + serial = dbus_message_get_serial (message); + if (serial == 0) + { + serial = _dbus_connection_get_next_client_serial (connection); + dbus_message_set_serial (message, serial); + } + + if (!_dbus_pending_call_set_timeout_error_unlocked (pending, message, serial)) + goto error; + + /* Insert the serial in the pending replies hash; + * hash takes a refcount on DBusPendingCall. + * Also, add the timeout. + */ + if (!_dbus_connection_attach_pending_call_unlocked (connection, + pending)) + goto error; + + if (!_dbus_connection_send_unlocked_no_update (connection, message, NULL)) + { + _dbus_connection_detach_pending_call_and_unlock (connection, + pending); + goto error_unlocked; + } + + if (pending_return) + *pending_return = pending; /* hand off refcount */ + else + { + _dbus_connection_detach_pending_call_unlocked (connection, pending); + /* we still have a ref to the pending call in this case, we unref + * after unlocking, below + */ + } + + status = _dbus_connection_get_dispatch_status_unlocked (connection); + + /* this calls out to user code */ + _dbus_connection_update_dispatch_status_and_unlock (connection, status); + + if (pending_return == NULL) + dbus_pending_call_unref (pending); + + return TRUE; + + error: + CONNECTION_UNLOCK (connection); + error_unlocked: + dbus_pending_call_unref (pending); + return FALSE; +} + +/** + * Sends a message and blocks a certain time period while waiting for + * a reply. This function does not reenter the main loop, + * i.e. messages other than the reply are queued up but not + * processed. This function is used to invoke method calls on a + * remote object. + * + * If a normal reply is received, it is returned, and removed from the + * incoming message queue. If it is not received, #NULL is returned + * and the error is set to #DBUS_ERROR_NO_REPLY. If an error reply is + * received, it is converted to a #DBusError and returned as an error, + * then the reply message is deleted and #NULL is returned. If + * something else goes wrong, result is set to whatever is + * appropriate, such as #DBUS_ERROR_NO_MEMORY or + * #DBUS_ERROR_DISCONNECTED. + * + * @warning While this function blocks the calling thread will not be + * processing the incoming message queue. This means you can end up + * deadlocked if the application you're talking to needs you to reply + * to a method. To solve this, either avoid the situation, block in a + * separate thread from the main connection-dispatching thread, or use + * dbus_pending_call_set_notify() to avoid blocking. + * + * @param connection the connection + * @param message the message to send + * @param timeout_milliseconds timeout in milliseconds or -1 for default + * @param error return location for error message + * @returns the message that is the reply or #NULL with an error code if the + * function fails. + */ +DBusMessage* +dbus_connection_send_with_reply_and_block (DBusConnection *connection, + DBusMessage *message, + int timeout_milliseconds, + DBusError *error) +{ + DBusMessage *reply; + DBusPendingCall *pending; + + _dbus_return_val_if_fail (connection != NULL, NULL); + _dbus_return_val_if_fail (message != NULL, NULL); + _dbus_return_val_if_fail (timeout_milliseconds >= 0 || timeout_milliseconds == -1, NULL); + _dbus_return_val_if_error_is_set (error, NULL); + + if (!dbus_connection_send_with_reply (connection, message, + &pending, timeout_milliseconds)) + { + _DBUS_SET_OOM (error); + return NULL; + } + + if (pending == NULL) + { + dbus_set_error (error, DBUS_ERROR_DISCONNECTED, "Connection is closed"); + return NULL; + } + + dbus_pending_call_block (pending); + + reply = dbus_pending_call_steal_reply (pending); + dbus_pending_call_unref (pending); + + /* call_complete_and_unlock() called from pending_call_block() should + * always fill this in. + */ + _dbus_assert (reply != NULL); + + if (dbus_set_error_from_message (error, reply)) + { + dbus_message_unref (reply); + return NULL; + } + else + return reply; +} + +/** + * Blocks until the outgoing message queue is empty. + * Assumes connection lock already held. + * + * If you call this, you MUST call update_dispatch_status afterword... + * + * @param connection the connection. + */ +static DBusDispatchStatus +_dbus_connection_flush_unlocked (DBusConnection *connection) +{ + /* We have to specify DBUS_ITERATION_DO_READING here because + * otherwise we could have two apps deadlock if they are both doing + * a flush(), and the kernel buffers fill up. This could change the + * dispatch status. + */ + DBusDispatchStatus status; + + HAVE_LOCK_CHECK (connection); + + while (connection->n_outgoing > 0 && + _dbus_connection_get_is_connected_unlocked (connection)) + { + _dbus_verbose ("doing iteration in %s\n", _DBUS_FUNCTION_NAME); + HAVE_LOCK_CHECK (connection); + _dbus_connection_do_iteration_unlocked (connection, + DBUS_ITERATION_DO_READING | + DBUS_ITERATION_DO_WRITING | + DBUS_ITERATION_BLOCK, + -1); + } + + HAVE_LOCK_CHECK (connection); + _dbus_verbose ("%s middle\n", _DBUS_FUNCTION_NAME); + status = _dbus_connection_get_dispatch_status_unlocked (connection); + + HAVE_LOCK_CHECK (connection); + return status; +} + +/** + * Blocks until the outgoing message queue is empty. + * + * @param connection the connection. + */ +void +dbus_connection_flush (DBusConnection *connection) +{ + /* We have to specify DBUS_ITERATION_DO_READING here because + * otherwise we could have two apps deadlock if they are both doing + * a flush(), and the kernel buffers fill up. This could change the + * dispatch status. + */ + DBusDispatchStatus status; + + _dbus_return_if_fail (connection != NULL); + + CONNECTION_LOCK (connection); + + status = _dbus_connection_flush_unlocked (connection); + + HAVE_LOCK_CHECK (connection); + /* Unlocks and calls out to user code */ + _dbus_connection_update_dispatch_status_and_unlock (connection, status); + + _dbus_verbose ("%s end\n", _DBUS_FUNCTION_NAME); +} + +/** + * This function implements dbus_connection_read_write_dispatch() and + * dbus_connection_read_write() (they pass a different value for the + * dispatch parameter). + * + * @param connection the connection + * @param timeout_milliseconds max time to block or -1 for infinite + * @param dispatch dispatch new messages or leave them on the incoming queue + * @returns #TRUE if the disconnect message has not been processed + */ +static dbus_bool_t +_dbus_connection_read_write_dispatch (DBusConnection *connection, + int timeout_milliseconds, + dbus_bool_t dispatch) +{ + DBusDispatchStatus dstatus; + dbus_bool_t progress_possible; + + /* Need to grab a ref here in case we're a private connection and + * the user drops the last ref in a handler we call; see bug + * https://bugs.freedesktop.org/show_bug.cgi?id=15635 + */ + dbus_connection_ref (connection); + dstatus = dbus_connection_get_dispatch_status (connection); + + if (dispatch && dstatus == DBUS_DISPATCH_DATA_REMAINS) + { + _dbus_verbose ("doing dispatch in %s\n", _DBUS_FUNCTION_NAME); + dbus_connection_dispatch (connection); + CONNECTION_LOCK (connection); + } + else if (dstatus == DBUS_DISPATCH_NEED_MEMORY) + { + _dbus_verbose ("pausing for memory in %s\n", _DBUS_FUNCTION_NAME); + _dbus_memory_pause_based_on_timeout (timeout_milliseconds); + CONNECTION_LOCK (connection); + } + else + { + CONNECTION_LOCK (connection); + if (_dbus_connection_get_is_connected_unlocked (connection)) + { + _dbus_verbose ("doing iteration in %s\n", _DBUS_FUNCTION_NAME); + _dbus_connection_do_iteration_unlocked (connection, + DBUS_ITERATION_DO_READING | + DBUS_ITERATION_DO_WRITING | + DBUS_ITERATION_BLOCK, + timeout_milliseconds); + } + } + + HAVE_LOCK_CHECK (connection); + /* If we can dispatch, we can make progress until the Disconnected message + * has been processed; if we can only read/write, we can make progress + * as long as the transport is open. + */ + if (dispatch) + progress_possible = connection->n_incoming != 0 || + connection->disconnect_message_link != NULL; + else + progress_possible = _dbus_connection_get_is_connected_unlocked (connection); + + CONNECTION_UNLOCK (connection); + + dbus_connection_unref (connection); + + return progress_possible; /* TRUE if we can make more progress */ +} + + +/** + * This function is intended for use with applications that don't want + * to write a main loop and deal with #DBusWatch and #DBusTimeout. An + * example usage would be: + * + * @code + * while (dbus_connection_read_write_dispatch (connection, -1)) + * ; // empty loop body + * @endcode + * + * In this usage you would normally have set up a filter function to look + * at each message as it is dispatched. The loop terminates when the last + * message from the connection (the disconnected signal) is processed. + * + * If there are messages to dispatch, this function will + * dbus_connection_dispatch() once, and return. If there are no + * messages to dispatch, this function will block until it can read or + * write, then read or write, then return. + * + * The way to think of this function is that it either makes some sort + * of progress, or it blocks. Note that, while it is blocked on I/O, it + * cannot be interrupted (even by other threads), which makes this function + * unsuitable for applications that do more than just react to received + * messages. + * + * The return value indicates whether the disconnect message has been + * processed, NOT whether the connection is connected. This is + * important because even after disconnecting, you want to process any + * messages you received prior to the disconnect. + * + * @param connection the connection + * @param timeout_milliseconds max time to block or -1 for infinite + * @returns #TRUE if the disconnect message has not been processed + */ +dbus_bool_t +dbus_connection_read_write_dispatch (DBusConnection *connection, + int timeout_milliseconds) +{ + _dbus_return_val_if_fail (connection != NULL, FALSE); + _dbus_return_val_if_fail (timeout_milliseconds >= 0 || timeout_milliseconds == -1, FALSE); + return _dbus_connection_read_write_dispatch(connection, timeout_milliseconds, TRUE); +} + +/** + * This function is intended for use with applications that don't want to + * write a main loop and deal with #DBusWatch and #DBusTimeout. See also + * dbus_connection_read_write_dispatch(). + * + * As long as the connection is open, this function will block until it can + * read or write, then read or write, then return #TRUE. + * + * If the connection is closed, the function returns #FALSE. + * + * The return value indicates whether reading or writing is still + * possible, i.e. whether the connection is connected. + * + * Note that even after disconnection, messages may remain in the + * incoming queue that need to be + * processed. dbus_connection_read_write_dispatch() dispatches + * incoming messages for you; with dbus_connection_read_write() you + * have to arrange to drain the incoming queue yourself. + * + * @param connection the connection + * @param timeout_milliseconds max time to block or -1 for infinite + * @returns #TRUE if still connected + */ +dbus_bool_t +dbus_connection_read_write (DBusConnection *connection, + int timeout_milliseconds) +{ + _dbus_return_val_if_fail (connection != NULL, FALSE); + _dbus_return_val_if_fail (timeout_milliseconds >= 0 || timeout_milliseconds == -1, FALSE); + return _dbus_connection_read_write_dispatch(connection, timeout_milliseconds, FALSE); +} + +/* We need to call this anytime we pop the head of the queue, and then + * update_dispatch_status_and_unlock needs to be called afterward + * which will "process" the disconnected message and set + * disconnected_message_processed. + */ +static void +check_disconnected_message_arrived_unlocked (DBusConnection *connection, + DBusMessage *head_of_queue) +{ + HAVE_LOCK_CHECK (connection); + + /* checking that the link is NULL is an optimization to avoid the is_signal call */ + if (connection->disconnect_message_link == NULL && + dbus_message_is_signal (head_of_queue, + DBUS_INTERFACE_LOCAL, + "Disconnected")) + { + connection->disconnected_message_arrived = TRUE; + } +} + +/** + * Returns the first-received message from the incoming message queue, + * leaving it in the queue. If the queue is empty, returns #NULL. + * + * The caller does not own a reference to the returned message, and + * must either return it using dbus_connection_return_message() or + * keep it after calling dbus_connection_steal_borrowed_message(). No + * one can get at the message while its borrowed, so return it as + * quickly as possible and don't keep a reference to it after + * returning it. If you need to keep the message, make a copy of it. + * + * dbus_connection_dispatch() will block if called while a borrowed + * message is outstanding; only one piece of code can be playing with + * the incoming queue at a time. This function will block if called + * during a dbus_connection_dispatch(). + * + * @param connection the connection. + * @returns next message in the incoming queue. + */ +DBusMessage* +dbus_connection_borrow_message (DBusConnection *connection) +{ + DBusDispatchStatus status; + DBusMessage *message; + + _dbus_return_val_if_fail (connection != NULL, NULL); + + _dbus_verbose ("%s start\n", _DBUS_FUNCTION_NAME); + + /* this is called for the side effect that it queues + * up any messages from the transport + */ + status = dbus_connection_get_dispatch_status (connection); + if (status != DBUS_DISPATCH_DATA_REMAINS) + return NULL; + + CONNECTION_LOCK (connection); + + _dbus_connection_acquire_dispatch (connection); + + /* While a message is outstanding, the dispatch lock is held */ + _dbus_assert (connection->message_borrowed == NULL); + + connection->message_borrowed = _dbus_list_get_first (&connection->incoming_messages); + + message = connection->message_borrowed; + + check_disconnected_message_arrived_unlocked (connection, message); + + /* Note that we KEEP the dispatch lock until the message is returned */ + if (message == NULL) + _dbus_connection_release_dispatch (connection); + + CONNECTION_UNLOCK (connection); + + /* We don't update dispatch status until it's returned or stolen */ + + return message; +} + +/** + * Used to return a message after peeking at it using + * dbus_connection_borrow_message(). Only called if + * message from dbus_connection_borrow_message() was non-#NULL. + * + * @param connection the connection + * @param message the message from dbus_connection_borrow_message() + */ +void +dbus_connection_return_message (DBusConnection *connection, + DBusMessage *message) +{ + DBusDispatchStatus status; + + _dbus_return_if_fail (connection != NULL); + _dbus_return_if_fail (message != NULL); + _dbus_return_if_fail (message == connection->message_borrowed); + _dbus_return_if_fail (connection->dispatch_acquired); + + CONNECTION_LOCK (connection); + + _dbus_assert (message == connection->message_borrowed); + + connection->message_borrowed = NULL; + + _dbus_connection_release_dispatch (connection); + + status = _dbus_connection_get_dispatch_status_unlocked (connection); + _dbus_connection_update_dispatch_status_and_unlock (connection, status); +} + +/** + * Used to keep a message after peeking at it using + * dbus_connection_borrow_message(). Before using this function, see + * the caveats/warnings in the documentation for + * dbus_connection_pop_message(). + * + * @param connection the connection + * @param message the message from dbus_connection_borrow_message() + */ +void +dbus_connection_steal_borrowed_message (DBusConnection *connection, + DBusMessage *message) +{ + DBusMessage *pop_message; + DBusDispatchStatus status; + + _dbus_return_if_fail (connection != NULL); + _dbus_return_if_fail (message != NULL); + _dbus_return_if_fail (message == connection->message_borrowed); + _dbus_return_if_fail (connection->dispatch_acquired); + + CONNECTION_LOCK (connection); + + _dbus_assert (message == connection->message_borrowed); + + pop_message = _dbus_list_pop_first (&connection->incoming_messages); + _dbus_assert (message == pop_message); + + connection->n_incoming -= 1; + + _dbus_verbose ("Incoming message %p stolen from queue, %d incoming\n", + message, connection->n_incoming); + + connection->message_borrowed = NULL; + + _dbus_connection_release_dispatch (connection); + + status = _dbus_connection_get_dispatch_status_unlocked (connection); + _dbus_connection_update_dispatch_status_and_unlock (connection, status); +} + +/* See dbus_connection_pop_message, but requires the caller to own + * the lock before calling. May drop the lock while running. + */ +static DBusList* +_dbus_connection_pop_message_link_unlocked (DBusConnection *connection) +{ + HAVE_LOCK_CHECK (connection); + + _dbus_assert (connection->message_borrowed == NULL); + + if (connection->n_incoming > 0) + { + DBusList *link; + + link = _dbus_list_pop_first_link (&connection->incoming_messages); + connection->n_incoming -= 1; + + _dbus_verbose ("Message %p (%d %s %s %s '%s') removed from incoming queue %p, %d incoming\n", + link->data, + dbus_message_get_type (link->data), + dbus_message_get_path (link->data) ? + dbus_message_get_path (link->data) : + "no path", + dbus_message_get_interface (link->data) ? + dbus_message_get_interface (link->data) : + "no interface", + dbus_message_get_member (link->data) ? + dbus_message_get_member (link->data) : + "no member", + dbus_message_get_signature (link->data), + connection, connection->n_incoming); + + check_disconnected_message_arrived_unlocked (connection, link->data); + + return link; + } + else + return NULL; +} + +/* See dbus_connection_pop_message, but requires the caller to own + * the lock before calling. May drop the lock while running. + */ +static DBusMessage* +_dbus_connection_pop_message_unlocked (DBusConnection *connection) +{ + DBusList *link; + + HAVE_LOCK_CHECK (connection); + + link = _dbus_connection_pop_message_link_unlocked (connection); + + if (link != NULL) + { + DBusMessage *message; + + message = link->data; + + _dbus_list_free_link (link); + + return message; + } + else + return NULL; +} + +static void +_dbus_connection_putback_message_link_unlocked (DBusConnection *connection, + DBusList *message_link) +{ + HAVE_LOCK_CHECK (connection); + + _dbus_assert (message_link != NULL); + /* You can't borrow a message while a link is outstanding */ + _dbus_assert (connection->message_borrowed == NULL); + /* We had to have the dispatch lock across the pop/putback */ + _dbus_assert (connection->dispatch_acquired); + + _dbus_list_prepend_link (&connection->incoming_messages, + message_link); + connection->n_incoming += 1; + + _dbus_verbose ("Message %p (%d %s %s '%s') put back into queue %p, %d incoming\n", + message_link->data, + dbus_message_get_type (message_link->data), + dbus_message_get_interface (message_link->data) ? + dbus_message_get_interface (message_link->data) : + "no interface", + dbus_message_get_member (message_link->data) ? + dbus_message_get_member (message_link->data) : + "no member", + dbus_message_get_signature (message_link->data), + connection, connection->n_incoming); +} + +/** + * Returns the first-received message from the incoming message queue, + * removing it from the queue. The caller owns a reference to the + * returned message. If the queue is empty, returns #NULL. + * + * This function bypasses any message handlers that are registered, + * and so using it is usually wrong. Instead, let the main loop invoke + * dbus_connection_dispatch(). Popping messages manually is only + * useful in very simple programs that don't share a #DBusConnection + * with any libraries or other modules. + * + * There is a lock that covers all ways of accessing the incoming message + * queue, so dbus_connection_dispatch(), dbus_connection_pop_message(), + * dbus_connection_borrow_message(), etc. will all block while one of the others + * in the group is running. + * + * @param connection the connection. + * @returns next message in the incoming queue. + */ +DBusMessage* +dbus_connection_pop_message (DBusConnection *connection) +{ + DBusMessage *message; + DBusDispatchStatus status; + + _dbus_verbose ("%s start\n", _DBUS_FUNCTION_NAME); + + /* this is called for the side effect that it queues + * up any messages from the transport + */ + status = dbus_connection_get_dispatch_status (connection); + if (status != DBUS_DISPATCH_DATA_REMAINS) + return NULL; + + CONNECTION_LOCK (connection); + _dbus_connection_acquire_dispatch (connection); + HAVE_LOCK_CHECK (connection); + + message = _dbus_connection_pop_message_unlocked (connection); + + _dbus_verbose ("Returning popped message %p\n", message); + + _dbus_connection_release_dispatch (connection); + + status = _dbus_connection_get_dispatch_status_unlocked (connection); + _dbus_connection_update_dispatch_status_and_unlock (connection, status); + + return message; +} + +/** + * Acquire the dispatcher. This is a separate lock so the main + * connection lock can be dropped to call out to application dispatch + * handlers. + * + * @param connection the connection. + */ +static void +_dbus_connection_acquire_dispatch (DBusConnection *connection) +{ + HAVE_LOCK_CHECK (connection); + + _dbus_connection_ref_unlocked (connection); + CONNECTION_UNLOCK (connection); + + _dbus_verbose ("%s locking dispatch_mutex\n", _DBUS_FUNCTION_NAME); + _dbus_mutex_lock (connection->dispatch_mutex); + + while (connection->dispatch_acquired) + { + _dbus_verbose ("%s waiting for dispatch to be acquirable\n", _DBUS_FUNCTION_NAME); + _dbus_condvar_wait (connection->dispatch_cond, + connection->dispatch_mutex); + } + + _dbus_assert (!connection->dispatch_acquired); + + connection->dispatch_acquired = TRUE; + + _dbus_verbose ("%s unlocking dispatch_mutex\n", _DBUS_FUNCTION_NAME); + _dbus_mutex_unlock (connection->dispatch_mutex); + + CONNECTION_LOCK (connection); + _dbus_connection_unref_unlocked (connection); +} + +/** + * Release the dispatcher when you're done with it. Only call + * after you've acquired the dispatcher. Wakes up at most one + * thread currently waiting to acquire the dispatcher. + * + * @param connection the connection. + */ +static void +_dbus_connection_release_dispatch (DBusConnection *connection) +{ + HAVE_LOCK_CHECK (connection); + + _dbus_verbose ("%s locking dispatch_mutex\n", _DBUS_FUNCTION_NAME); + _dbus_mutex_lock (connection->dispatch_mutex); + + _dbus_assert (connection->dispatch_acquired); + + connection->dispatch_acquired = FALSE; + _dbus_condvar_wake_one (connection->dispatch_cond); + + _dbus_verbose ("%s unlocking dispatch_mutex\n", _DBUS_FUNCTION_NAME); + _dbus_mutex_unlock (connection->dispatch_mutex); +} + +static void +_dbus_connection_failed_pop (DBusConnection *connection, + DBusList *message_link) +{ + _dbus_list_prepend_link (&connection->incoming_messages, + message_link); + connection->n_incoming += 1; +} + +/* Note this may be called multiple times since we don't track whether we already did it */ +static void +notify_disconnected_unlocked (DBusConnection *connection) +{ + HAVE_LOCK_CHECK (connection); + + /* Set the weakref in dbus-bus.c to NULL, so nobody will get a disconnected + * connection from dbus_bus_get(). We make the same guarantee for + * dbus_connection_open() but in a different way since we don't want to + * unref right here; we instead check for connectedness before returning + * the connection from the hash. + */ + _dbus_bus_notify_shared_connection_disconnected_unlocked (connection); + + /* Dump the outgoing queue, we aren't going to be able to + * send it now, and we'd like accessors like + * dbus_connection_get_outgoing_size() to be accurate. + */ + if (connection->n_outgoing > 0) + { + DBusList *link; + + _dbus_verbose ("Dropping %d outgoing messages since we're disconnected\n", + connection->n_outgoing); + + while ((link = _dbus_list_get_last_link (&connection->outgoing_messages))) + { + _dbus_connection_message_sent (connection, link->data); + } + } +} + +/* Note this may be called multiple times since we don't track whether we already did it */ +static DBusDispatchStatus +notify_disconnected_and_dispatch_complete_unlocked (DBusConnection *connection) +{ + HAVE_LOCK_CHECK (connection); + + if (connection->disconnect_message_link != NULL) + { + _dbus_verbose ("Sending disconnect message from %s\n", + _DBUS_FUNCTION_NAME); + + /* If we have pending calls, queue their timeouts - we want the Disconnected + * to be the last message, after these timeouts. + */ + connection_timeout_and_complete_all_pending_calls_unlocked (connection); + + /* We haven't sent the disconnect message already, + * and all real messages have been queued up. + */ + _dbus_connection_queue_synthesized_message_link (connection, + connection->disconnect_message_link); + connection->disconnect_message_link = NULL; + + return DBUS_DISPATCH_DATA_REMAINS; + } + + return DBUS_DISPATCH_COMPLETE; +} + +static DBusDispatchStatus +_dbus_connection_get_dispatch_status_unlocked (DBusConnection *connection) +{ + HAVE_LOCK_CHECK (connection); + + if (connection->n_incoming > 0) + return DBUS_DISPATCH_DATA_REMAINS; + else if (!_dbus_transport_queue_messages (connection->transport)) + return DBUS_DISPATCH_NEED_MEMORY; + else + { + DBusDispatchStatus status; + dbus_bool_t is_connected; + + status = _dbus_transport_get_dispatch_status (connection->transport); + is_connected = _dbus_transport_get_is_connected (connection->transport); + + _dbus_verbose ("dispatch status = %s is_connected = %d\n", + DISPATCH_STATUS_NAME (status), is_connected); + + if (!is_connected) + { + /* It's possible this would be better done by having an explicit + * notification from _dbus_transport_disconnect() that would + * synchronously do this, instead of waiting for the next dispatch + * status check. However, probably not good to change until it causes + * a problem. + */ + notify_disconnected_unlocked (connection); + + /* I'm not sure this is needed; the idea is that we want to + * queue the Disconnected only after we've read all the + * messages, but if we're disconnected maybe we are guaranteed + * to have read them all ? + */ + if (status == DBUS_DISPATCH_COMPLETE) + status = notify_disconnected_and_dispatch_complete_unlocked (connection); + } + + if (status != DBUS_DISPATCH_COMPLETE) + return status; + else if (connection->n_incoming > 0) + return DBUS_DISPATCH_DATA_REMAINS; + else + return DBUS_DISPATCH_COMPLETE; + } +} + +static void +_dbus_connection_update_dispatch_status_and_unlock (DBusConnection *connection, + DBusDispatchStatus new_status) +{ + dbus_bool_t changed; + DBusDispatchStatusFunction function; + void *data; + + HAVE_LOCK_CHECK (connection); + + _dbus_connection_ref_unlocked (connection); + + changed = new_status != connection->last_dispatch_status; + + connection->last_dispatch_status = new_status; + + function = connection->dispatch_status_function; + data = connection->dispatch_status_data; + + if (connection->disconnected_message_arrived && + !connection->disconnected_message_processed) + { + connection->disconnected_message_processed = TRUE; + + /* this does an unref, but we have a ref + * so we should not run the finalizer here + * inside the lock. + */ + connection_forget_shared_unlocked (connection); + + if (connection->exit_on_disconnect) + { + CONNECTION_UNLOCK (connection); + + _dbus_verbose ("Exiting on Disconnected signal\n"); + _dbus_exit (1); + _dbus_assert_not_reached ("Call to exit() returned"); + } + } + + /* We drop the lock */ + CONNECTION_UNLOCK (connection); + + if (changed && function) + { + _dbus_verbose ("Notifying of change to dispatch status of %p now %d (%s)\n", + connection, new_status, + DISPATCH_STATUS_NAME (new_status)); + (* function) (connection, new_status, data); + } + + dbus_connection_unref (connection); +} + +/** + * Gets the current state of the incoming message queue. + * #DBUS_DISPATCH_DATA_REMAINS indicates that the message queue + * may contain messages. #DBUS_DISPATCH_COMPLETE indicates that the + * incoming queue is empty. #DBUS_DISPATCH_NEED_MEMORY indicates that + * there could be data, but we can't know for sure without more + * memory. + * + * To process the incoming message queue, use dbus_connection_dispatch() + * or (in rare cases) dbus_connection_pop_message(). + * + * Note, #DBUS_DISPATCH_DATA_REMAINS really means that either we + * have messages in the queue, or we have raw bytes buffered up + * that need to be parsed. When these bytes are parsed, they + * may not add up to an entire message. Thus, it's possible + * to see a status of #DBUS_DISPATCH_DATA_REMAINS but not + * have a message yet. + * + * In particular this happens on initial connection, because all sorts + * of authentication protocol stuff has to be parsed before the + * first message arrives. + * + * @param connection the connection. + * @returns current dispatch status + */ +DBusDispatchStatus +dbus_connection_get_dispatch_status (DBusConnection *connection) +{ + DBusDispatchStatus status; + + _dbus_return_val_if_fail (connection != NULL, DBUS_DISPATCH_COMPLETE); + + _dbus_verbose ("%s start\n", _DBUS_FUNCTION_NAME); + + CONNECTION_LOCK (connection); + + status = _dbus_connection_get_dispatch_status_unlocked (connection); + + CONNECTION_UNLOCK (connection); + + return status; +} + +/** + * Filter funtion for handling the Peer standard interface. + */ +static DBusHandlerResult +_dbus_connection_peer_filter_unlocked_no_update (DBusConnection *connection, + DBusMessage *message) +{ + if (connection->route_peer_messages && dbus_message_get_destination (message) != NULL) + { + /* This means we're letting the bus route this message */ + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + else if (dbus_message_is_method_call (message, + DBUS_INTERFACE_PEER, + "Ping")) + { + DBusMessage *ret; + dbus_bool_t sent; + + ret = dbus_message_new_method_return (message); + if (ret == NULL) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + sent = _dbus_connection_send_unlocked_no_update (connection, ret, NULL); + + dbus_message_unref (ret); + + if (!sent) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + return DBUS_HANDLER_RESULT_HANDLED; + } + else if (dbus_message_is_method_call (message, + DBUS_INTERFACE_PEER, + "GetMachineId")) + { + DBusMessage *ret; + dbus_bool_t sent; + DBusString uuid; + + ret = dbus_message_new_method_return (message); + if (ret == NULL) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + sent = FALSE; + _dbus_string_init (&uuid); + if (_dbus_get_local_machine_uuid_encoded (&uuid)) + { + const char *v_STRING = _dbus_string_get_const_data (&uuid); + if (dbus_message_append_args (ret, + DBUS_TYPE_STRING, &v_STRING, + DBUS_TYPE_INVALID)) + { + sent = _dbus_connection_send_unlocked_no_update (connection, ret, NULL); + } + } + _dbus_string_free (&uuid); + + dbus_message_unref (ret); + + if (!sent) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + return DBUS_HANDLER_RESULT_HANDLED; + } + else if (dbus_message_has_interface (message, DBUS_INTERFACE_PEER)) + { + /* We need to bounce anything else with this interface, otherwise apps + * could start extending the interface and when we added extensions + * here to DBusConnection we'd break those apps. + */ + + DBusMessage *ret; + dbus_bool_t sent; + + ret = dbus_message_new_error (message, + DBUS_ERROR_UNKNOWN_METHOD, + "Unknown method invoked on org.freedesktop.DBus.Peer interface"); + if (ret == NULL) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + sent = _dbus_connection_send_unlocked_no_update (connection, ret, NULL); + + dbus_message_unref (ret); + + if (!sent) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + return DBUS_HANDLER_RESULT_HANDLED; + } + else + { + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } +} + +/** +* Processes all builtin filter functions +* +* If the spec specifies a standard interface +* they should be processed from this method +**/ +static DBusHandlerResult +_dbus_connection_run_builtin_filters_unlocked_no_update (DBusConnection *connection, + DBusMessage *message) +{ + /* We just run one filter for now but have the option to run more + if the spec calls for it in the future */ + + return _dbus_connection_peer_filter_unlocked_no_update (connection, message); +} + +/** + * Processes any incoming data. + * + * If there's incoming raw data that has not yet been parsed, it is + * parsed, which may or may not result in adding messages to the + * incoming queue. + * + * The incoming data buffer is filled when the connection reads from + * its underlying transport (such as a socket). Reading usually + * happens in dbus_watch_handle() or dbus_connection_read_write(). + * + * If there are complete messages in the incoming queue, + * dbus_connection_dispatch() removes one message from the queue and + * processes it. Processing has three steps. + * + * First, any method replies are passed to #DBusPendingCall or + * dbus_connection_send_with_reply_and_block() in order to + * complete the pending method call. + * + * Second, any filters registered with dbus_connection_add_filter() + * are run. If any filter returns #DBUS_HANDLER_RESULT_HANDLED + * then processing stops after that filter. + * + * Third, if the message is a method call it is forwarded to + * any registered object path handlers added with + * dbus_connection_register_object_path() or + * dbus_connection_register_fallback(). + * + * A single call to dbus_connection_dispatch() will process at most + * one message; it will not clear the entire message queue. + * + * Be careful about calling dbus_connection_dispatch() from inside a + * message handler, i.e. calling dbus_connection_dispatch() + * recursively. If threads have been initialized with a recursive + * mutex function, then this will not deadlock; however, it can + * certainly confuse your application. + * + * @todo some FIXME in here about handling DBUS_HANDLER_RESULT_NEED_MEMORY + * + * @param connection the connection + * @returns dispatch status, see dbus_connection_get_dispatch_status() + */ +DBusDispatchStatus +dbus_connection_dispatch (DBusConnection *connection) +{ + DBusMessage *message; + DBusList *link, *filter_list_copy, *message_link; + DBusHandlerResult result; + DBusPendingCall *pending; + dbus_int32_t reply_serial; + DBusDispatchStatus status; + + _dbus_return_val_if_fail (connection != NULL, DBUS_DISPATCH_COMPLETE); + + _dbus_verbose ("%s\n", _DBUS_FUNCTION_NAME); + + CONNECTION_LOCK (connection); + status = _dbus_connection_get_dispatch_status_unlocked (connection); + if (status != DBUS_DISPATCH_DATA_REMAINS) + { + /* unlocks and calls out to user code */ + _dbus_connection_update_dispatch_status_and_unlock (connection, status); + return status; + } + + /* We need to ref the connection since the callback could potentially + * drop the last ref to it + */ + _dbus_connection_ref_unlocked (connection); + + _dbus_connection_acquire_dispatch (connection); + HAVE_LOCK_CHECK (connection); + + message_link = _dbus_connection_pop_message_link_unlocked (connection); + if (message_link == NULL) + { + /* another thread dispatched our stuff */ + + _dbus_verbose ("another thread dispatched message (during acquire_dispatch above)\n"); + + _dbus_connection_release_dispatch (connection); + + status = _dbus_connection_get_dispatch_status_unlocked (connection); + + _dbus_connection_update_dispatch_status_and_unlock (connection, status); + + dbus_connection_unref (connection); + + return status; + } + + message = message_link->data; + + _dbus_verbose (" dispatching message %p (%d %s %s '%s')\n", + message, + dbus_message_get_type (message), + 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_signature (message)); + + result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + /* Pending call handling must be first, because if you do + * dbus_connection_send_with_reply_and_block() or + * dbus_pending_call_block() then no handlers/filters will be run on + * the reply. We want consistent semantics in the case where we + * dbus_connection_dispatch() the reply. + */ + + reply_serial = dbus_message_get_reply_serial (message); + pending = _dbus_hash_table_lookup_int (connection->pending_replies, + reply_serial); + if (pending) + { + _dbus_verbose ("Dispatching a pending reply\n"); + complete_pending_call_and_unlock (connection, pending, message); + pending = NULL; /* it's probably unref'd */ + + CONNECTION_LOCK (connection); + _dbus_verbose ("pending call completed in dispatch\n"); + result = DBUS_HANDLER_RESULT_HANDLED; + goto out; + } + + result = _dbus_connection_run_builtin_filters_unlocked_no_update (connection, message); + if (result != DBUS_HANDLER_RESULT_NOT_YET_HANDLED) + goto out; + + if (!_dbus_list_copy (&connection->filter_list, &filter_list_copy)) + { + _dbus_connection_release_dispatch (connection); + HAVE_LOCK_CHECK (connection); + + _dbus_connection_failed_pop (connection, message_link); + + /* unlocks and calls user code */ + _dbus_connection_update_dispatch_status_and_unlock (connection, + DBUS_DISPATCH_NEED_MEMORY); + + if (pending) + dbus_pending_call_unref (pending); + dbus_connection_unref (connection); + + return DBUS_DISPATCH_NEED_MEMORY; + } + + _dbus_list_foreach (&filter_list_copy, + (DBusForeachFunction)_dbus_message_filter_ref, + NULL); + + /* We're still protected from dispatch() reentrancy here + * since we acquired the dispatcher + */ + CONNECTION_UNLOCK (connection); + + link = _dbus_list_get_first_link (&filter_list_copy); + while (link != NULL) + { + DBusMessageFilter *filter = link->data; + DBusList *next = _dbus_list_get_next_link (&filter_list_copy, link); + + if (filter->function == NULL) + { + _dbus_verbose (" filter was removed in a callback function\n"); + link = next; + continue; + } + + _dbus_verbose (" running filter on message %p\n", message); + result = (* filter->function) (connection, message, filter->user_data); + + if (result != DBUS_HANDLER_RESULT_NOT_YET_HANDLED) + break; + + link = next; + } + + _dbus_list_foreach (&filter_list_copy, + (DBusForeachFunction)_dbus_message_filter_unref, + NULL); + _dbus_list_clear (&filter_list_copy); + + CONNECTION_LOCK (connection); + + if (result == DBUS_HANDLER_RESULT_NEED_MEMORY) + { + _dbus_verbose ("No memory in %s\n", _DBUS_FUNCTION_NAME); + goto out; + } + else if (result == DBUS_HANDLER_RESULT_HANDLED) + { + _dbus_verbose ("filter handled message in dispatch\n"); + goto out; + } + + /* We're still protected from dispatch() reentrancy here + * since we acquired the dispatcher + */ + _dbus_verbose (" running object path dispatch on message %p (%d %s %s '%s')\n", + message, + dbus_message_get_type (message), + 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_signature (message)); + + HAVE_LOCK_CHECK (connection); + result = _dbus_object_tree_dispatch_and_unlock (connection->objects, + message); + + CONNECTION_LOCK (connection); + + if (result != DBUS_HANDLER_RESULT_NOT_YET_HANDLED) + { + _dbus_verbose ("object tree handled message in dispatch\n"); + goto out; + } + + if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_CALL) + { + DBusMessage *reply; + DBusString str; + DBusPreallocatedSend *preallocated; + + _dbus_verbose (" sending error %s\n", + DBUS_ERROR_UNKNOWN_METHOD); + + if (!_dbus_string_init (&str)) + { + result = DBUS_HANDLER_RESULT_NEED_MEMORY; + _dbus_verbose ("no memory for error string in dispatch\n"); + goto out; + } + + if (!_dbus_string_append_printf (&str, + "Method \"%s\" with signature \"%s\" on interface \"%s\" doesn't exist\n", + dbus_message_get_member (message), + dbus_message_get_signature (message), + dbus_message_get_interface (message))) + { + _dbus_string_free (&str); + result = DBUS_HANDLER_RESULT_NEED_MEMORY; + _dbus_verbose ("no memory for error string in dispatch\n"); + goto out; + } + + reply = dbus_message_new_error (message, + DBUS_ERROR_UNKNOWN_METHOD, + _dbus_string_get_const_data (&str)); + _dbus_string_free (&str); + + if (reply == NULL) + { + result = DBUS_HANDLER_RESULT_NEED_MEMORY; + _dbus_verbose ("no memory for error reply in dispatch\n"); + goto out; + } + + preallocated = _dbus_connection_preallocate_send_unlocked (connection); + + if (preallocated == NULL) + { + dbus_message_unref (reply); + result = DBUS_HANDLER_RESULT_NEED_MEMORY; + _dbus_verbose ("no memory for error send in dispatch\n"); + goto out; + } + + _dbus_connection_send_preallocated_unlocked_no_update (connection, preallocated, + reply, NULL); + + dbus_message_unref (reply); + + result = DBUS_HANDLER_RESULT_HANDLED; + } + + _dbus_verbose (" done dispatching %p (%d %s %s '%s') on connection %p\n", message, + dbus_message_get_type (message), + 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_signature (message), + connection); + + out: + if (result == DBUS_HANDLER_RESULT_NEED_MEMORY) + { + _dbus_verbose ("out of memory in %s\n", _DBUS_FUNCTION_NAME); + + /* Put message back, and we'll start over. + * Yes this means handlers must be idempotent if they + * don't return HANDLED; c'est la vie. + */ + _dbus_connection_putback_message_link_unlocked (connection, + message_link); + } + else + { + _dbus_verbose (" ... done dispatching in %s\n", _DBUS_FUNCTION_NAME); + + _dbus_list_free_link (message_link); + dbus_message_unref (message); /* don't want the message to count in max message limits + * in computing dispatch status below + */ + } + + _dbus_connection_release_dispatch (connection); + HAVE_LOCK_CHECK (connection); + + _dbus_verbose ("%s before final status update\n", _DBUS_FUNCTION_NAME); + status = _dbus_connection_get_dispatch_status_unlocked (connection); + + /* unlocks and calls user code */ + _dbus_connection_update_dispatch_status_and_unlock (connection, status); + + dbus_connection_unref (connection); + + return status; +} + +/** + * Sets the watch functions for the connection. These functions are + * responsible for making the application's main loop aware of file + * descriptors that need to be monitored for events, using select() or + * poll(). When using Qt, typically the DBusAddWatchFunction would + * create a QSocketNotifier. When using GLib, the DBusAddWatchFunction + * could call g_io_add_watch(), or could be used as part of a more + * elaborate GSource. Note that when a watch is added, it may + * not be enabled. + * + * The DBusWatchToggledFunction notifies the application that the + * watch has been enabled or disabled. Call dbus_watch_get_enabled() + * to check this. A disabled watch should have no effect, and enabled + * watch should be added to the main loop. This feature is used + * instead of simply adding/removing the watch because + * enabling/disabling can be done without memory allocation. The + * toggled function may be NULL if a main loop re-queries + * dbus_watch_get_enabled() every time anyway. + * + * The DBusWatch can be queried for the file descriptor to watch using + * dbus_watch_get_unix_fd() or dbus_watch_get_socket(), and for the + * events to watch for using dbus_watch_get_flags(). The flags + * returned by dbus_watch_get_flags() will only contain + * DBUS_WATCH_READABLE and DBUS_WATCH_WRITABLE, never + * DBUS_WATCH_HANGUP or DBUS_WATCH_ERROR; all watches implicitly + * include a watch for hangups, errors, and other exceptional + * conditions. + * + * Once a file descriptor becomes readable or writable, or an exception + * occurs, dbus_watch_handle() should be called to + * notify the connection of the file descriptor's condition. + * + * dbus_watch_handle() cannot be called during the + * DBusAddWatchFunction, as the connection will not be ready to handle + * that watch yet. + * + * It is not allowed to reference a DBusWatch after it has been passed + * to remove_function. + * + * If #FALSE is returned due to lack of memory, the failure may be due + * to a #FALSE return from the new add_function. If so, the + * add_function may have been called successfully one or more times, + * but the remove_function will also have been called to remove any + * successful adds. i.e. if #FALSE is returned the net result + * should be that dbus_connection_set_watch_functions() has no effect, + * but the add_function and remove_function may have been called. + * + * @todo We need to drop the lock when we call the + * add/remove/toggled functions which can be a side effect + * of setting the watch functions. + * + * @param connection the connection. + * @param add_function function to begin monitoring a new descriptor. + * @param remove_function function to stop monitoring a descriptor. + * @param toggled_function function to notify of enable/disable + * @param data data to pass to add_function and remove_function. + * @param free_data_function function to be called to free the data. + * @returns #FALSE on failure (no memory) + */ +dbus_bool_t +dbus_connection_set_watch_functions (DBusConnection *connection, + DBusAddWatchFunction add_function, + DBusRemoveWatchFunction remove_function, + DBusWatchToggledFunction toggled_function, + void *data, + DBusFreeFunction free_data_function) +{ + dbus_bool_t retval; + DBusWatchList *watches; + + _dbus_return_val_if_fail (connection != NULL, FALSE); + + CONNECTION_LOCK (connection); + +#ifndef DBUS_DISABLE_CHECKS + if (connection->watches == NULL) + { + _dbus_warn_check_failed ("Re-entrant call to %s is not allowed\n", + _DBUS_FUNCTION_NAME); + return FALSE; + } +#endif + + /* ref connection for slightly better reentrancy */ + _dbus_connection_ref_unlocked (connection); + + /* This can call back into user code, and we need to drop the + * connection lock when it does. This is kind of a lame + * way to do it. + */ + watches = connection->watches; + connection->watches = NULL; + CONNECTION_UNLOCK (connection); + + retval = _dbus_watch_list_set_functions (watches, + add_function, remove_function, + toggled_function, + data, free_data_function); + CONNECTION_LOCK (connection); + connection->watches = watches; + + CONNECTION_UNLOCK (connection); + /* drop our paranoid refcount */ + dbus_connection_unref (connection); + + return retval; +} + +/** + * Sets the timeout functions for the connection. These functions are + * responsible for making the application's main loop aware of timeouts. + * When using Qt, typically the DBusAddTimeoutFunction would create a + * QTimer. When using GLib, the DBusAddTimeoutFunction would call + * g_timeout_add. + * + * The DBusTimeoutToggledFunction notifies the application that the + * timeout has been enabled or disabled. Call + * dbus_timeout_get_enabled() to check this. A disabled timeout should + * have no effect, and enabled timeout should be added to the main + * loop. This feature is used instead of simply adding/removing the + * timeout because enabling/disabling can be done without memory + * allocation. With Qt, QTimer::start() and QTimer::stop() can be used + * to enable and disable. The toggled function may be NULL if a main + * loop re-queries dbus_timeout_get_enabled() every time anyway. + * Whenever a timeout is toggled, its interval may change. + * + * The DBusTimeout can be queried for the timer interval using + * dbus_timeout_get_interval(). dbus_timeout_handle() should be called + * repeatedly, each time the interval elapses, starting after it has + * elapsed once. The timeout stops firing when it is removed with the + * given remove_function. The timer interval may change whenever the + * timeout is added, removed, or toggled. + * + * @param connection the connection. + * @param add_function function to add a timeout. + * @param remove_function function to remove a timeout. + * @param toggled_function function to notify of enable/disable + * @param data data to pass to add_function and remove_function. + * @param free_data_function function to be called to free the data. + * @returns #FALSE on failure (no memory) + */ +dbus_bool_t +dbus_connection_set_timeout_functions (DBusConnection *connection, + DBusAddTimeoutFunction add_function, + DBusRemoveTimeoutFunction remove_function, + DBusTimeoutToggledFunction toggled_function, + void *data, + DBusFreeFunction free_data_function) +{ + dbus_bool_t retval; + DBusTimeoutList *timeouts; + + _dbus_return_val_if_fail (connection != NULL, FALSE); + + CONNECTION_LOCK (connection); + +#ifndef DBUS_DISABLE_CHECKS + if (connection->timeouts == NULL) + { + _dbus_warn_check_failed ("Re-entrant call to %s is not allowed\n", + _DBUS_FUNCTION_NAME); + return FALSE; + } +#endif + + /* ref connection for slightly better reentrancy */ + _dbus_connection_ref_unlocked (connection); + + timeouts = connection->timeouts; + connection->timeouts = NULL; + CONNECTION_UNLOCK (connection); + + retval = _dbus_timeout_list_set_functions (timeouts, + add_function, remove_function, + toggled_function, + data, free_data_function); + CONNECTION_LOCK (connection); + connection->timeouts = timeouts; + + CONNECTION_UNLOCK (connection); + /* drop our paranoid refcount */ + dbus_connection_unref (connection); + + return retval; +} + +/** + * Sets the mainloop wakeup function for the connection. This function + * is responsible for waking up the main loop (if its sleeping in + * another thread) when some some change has happened to the + * connection that the mainloop needs to reconsider (e.g. a message + * has been queued for writing). When using Qt, this typically + * results in a call to QEventLoop::wakeUp(). When using GLib, it + * would call g_main_context_wakeup(). + * + * @param connection the connection. + * @param wakeup_main_function function to wake up the mainloop + * @param data data to pass wakeup_main_function + * @param free_data_function function to be called to free the data. + */ +void +dbus_connection_set_wakeup_main_function (DBusConnection *connection, + DBusWakeupMainFunction wakeup_main_function, + void *data, + DBusFreeFunction free_data_function) +{ + void *old_data; + DBusFreeFunction old_free_data; + + _dbus_return_if_fail (connection != NULL); + + CONNECTION_LOCK (connection); + old_data = connection->wakeup_main_data; + old_free_data = connection->free_wakeup_main_data; + + connection->wakeup_main_function = wakeup_main_function; + connection->wakeup_main_data = data; + connection->free_wakeup_main_data = free_data_function; + + CONNECTION_UNLOCK (connection); + + /* Callback outside the lock */ + if (old_free_data) + (*old_free_data) (old_data); +} + +/** + * Set a function to be invoked when the dispatch status changes. + * If the dispatch status is #DBUS_DISPATCH_DATA_REMAINS, then + * dbus_connection_dispatch() needs to be called to process incoming + * messages. However, dbus_connection_dispatch() MUST NOT BE CALLED + * from inside the DBusDispatchStatusFunction. Indeed, almost + * any reentrancy in this function is a bad idea. Instead, + * the DBusDispatchStatusFunction should simply save an indication + * that messages should be dispatched later, when the main loop + * is re-entered. + * + * If you don't set a dispatch status function, you have to be sure to + * dispatch on every iteration of your main loop, especially if + * dbus_watch_handle() or dbus_timeout_handle() were called. + * + * @param connection the connection + * @param function function to call on dispatch status changes + * @param data data for function + * @param free_data_function free the function data + */ +void +dbus_connection_set_dispatch_status_function (DBusConnection *connection, + DBusDispatchStatusFunction function, + void *data, + DBusFreeFunction free_data_function) +{ + void *old_data; + DBusFreeFunction old_free_data; + + _dbus_return_if_fail (connection != NULL); + + CONNECTION_LOCK (connection); + old_data = connection->dispatch_status_data; + old_free_data = connection->free_dispatch_status_data; + + connection->dispatch_status_function = function; + connection->dispatch_status_data = data; + connection->free_dispatch_status_data = free_data_function; + + CONNECTION_UNLOCK (connection); + + /* Callback outside the lock */ + if (old_free_data) + (*old_free_data) (old_data); +} + +/** + * Get the UNIX file descriptor of the connection, if any. This can + * be used for SELinux access control checks with getpeercon() for + * example. DO NOT read or write to the file descriptor, or try to + * select() on it; use DBusWatch for main loop integration. Not all + * connections will have a file descriptor. So for adding descriptors + * to the main loop, use dbus_watch_get_unix_fd() and so forth. + * + * If the connection is socket-based, you can also use + * dbus_connection_get_socket(), which will work on Windows too. + * This function always fails on Windows. + * + * Right now the returned descriptor is always a socket, but + * that is not guaranteed. + * + * @param connection the connection + * @param fd return location for the file descriptor. + * @returns #TRUE if fd is successfully obtained. + */ +dbus_bool_t +dbus_connection_get_unix_fd (DBusConnection *connection, + int *fd) +{ + _dbus_return_val_if_fail (connection != NULL, FALSE); + _dbus_return_val_if_fail (connection->transport != NULL, FALSE); + +#ifdef DBUS_WIN + /* FIXME do this on a lower level */ + return FALSE; +#endif + + return dbus_connection_get_socket(connection, fd); +} + +/** + * Gets the underlying Windows or UNIX socket file descriptor + * of the connection, if any. DO NOT read or write to the file descriptor, or try to + * select() on it; use DBusWatch for main loop integration. Not all + * connections will have a socket. So for adding descriptors + * to the main loop, use dbus_watch_get_socket() and so forth. + * + * If the connection is not socket-based, this function will return FALSE, + * even if the connection does have a file descriptor of some kind. + * i.e. this function always returns specifically a socket file descriptor. + * + * @param connection the connection + * @param fd return location for the file descriptor. + * @returns #TRUE if fd is successfully obtained. + */ +dbus_bool_t +dbus_connection_get_socket(DBusConnection *connection, + int *fd) +{ + dbus_bool_t retval; + + _dbus_return_val_if_fail (connection != NULL, FALSE); + _dbus_return_val_if_fail (connection->transport != NULL, FALSE); + + CONNECTION_LOCK (connection); + + retval = _dbus_transport_get_socket_fd (connection->transport, + fd); + + CONNECTION_UNLOCK (connection); + + return retval; +} + + +/** + * Gets the UNIX user ID of the connection if known. Returns #TRUE if + * the uid is filled in. Always returns #FALSE on non-UNIX platforms + * for now, though in theory someone could hook Windows to NIS or + * something. Always returns #FALSE prior to authenticating the + * connection. + * + * The UID is only read by servers from clients; clients can't usually + * get the UID of servers, because servers do not authenticate to + * clients. The returned UID is the UID the connection authenticated + * as. + * + * The message bus is a server and the apps connecting to the bus + * are clients. + * + * You can ask the bus to tell you the UID of another connection though + * if you like; this is done with dbus_bus_get_unix_user(). + * + * @param connection the connection + * @param uid return location for the user ID + * @returns #TRUE if uid is filled in with a valid user ID + */ +dbus_bool_t +dbus_connection_get_unix_user (DBusConnection *connection, + unsigned long *uid) +{ + dbus_bool_t result; + + _dbus_return_val_if_fail (connection != NULL, FALSE); + _dbus_return_val_if_fail (uid != NULL, FALSE); + + CONNECTION_LOCK (connection); + + if (!_dbus_transport_get_is_authenticated (connection->transport)) + result = FALSE; + else + result = _dbus_transport_get_unix_user (connection->transport, + uid); + +#ifdef DBUS_WIN + _dbus_assert (!result); +#endif + + CONNECTION_UNLOCK (connection); + + return result; +} + +/** + * Gets the process ID of the connection if any. + * Returns #TRUE if the pid is filled in. + * Always returns #FALSE prior to authenticating the + * connection. + * + * @param connection the connection + * @param pid return location for the process ID + * @returns #TRUE if uid is filled in with a valid process ID + */ +dbus_bool_t +dbus_connection_get_unix_process_id (DBusConnection *connection, + unsigned long *pid) +{ + dbus_bool_t result; + + _dbus_return_val_if_fail (connection != NULL, FALSE); + _dbus_return_val_if_fail (pid != NULL, FALSE); + + CONNECTION_LOCK (connection); + + if (!_dbus_transport_get_is_authenticated (connection->transport)) + result = FALSE; + else + result = _dbus_transport_get_unix_process_id (connection->transport, + pid); +#ifdef DBUS_WIN + _dbus_assert (!result); +#endif + + CONNECTION_UNLOCK (connection); + + return result; +} + +/** + * Gets the ADT audit data of the connection if any. + * Returns #TRUE if the structure pointer is returned. + * Always returns #FALSE prior to authenticating the + * connection. + * + * @param connection the connection + * @param data return location for audit data + * @returns #TRUE if audit data is filled in with a valid ucred pointer + */ +dbus_bool_t +dbus_connection_get_adt_audit_session_data (DBusConnection *connection, + void **data, + dbus_int32_t *data_size) +{ + dbus_bool_t result; + + _dbus_return_val_if_fail (connection != NULL, FALSE); + _dbus_return_val_if_fail (data != NULL, FALSE); + _dbus_return_val_if_fail (data_size != NULL, FALSE); + + CONNECTION_LOCK (connection); + + if (!_dbus_transport_get_is_authenticated (connection->transport)) + result = FALSE; + else + result = _dbus_transport_get_adt_audit_session_data (connection->transport, + data, + data_size); + CONNECTION_UNLOCK (connection); + + return result; +} + +/** + * Sets a predicate function used to determine whether a given user ID + * is allowed to connect. When an incoming connection has + * authenticated with a particular user ID, this function is called; + * if it returns #TRUE, the connection is allowed to proceed, + * otherwise the connection is disconnected. + * + * If the function is set to #NULL (as it is by default), then + * only the same UID as the server process will be allowed to + * connect. Also, root is always allowed to connect. + * + * On Windows, the function will be set and its free_data_function will + * be invoked when the connection is freed or a new function is set. + * However, the function will never be called, because there are + * no UNIX user ids to pass to it, or at least none of the existing + * auth protocols would allow authenticating as a UNIX user on Windows. + * + * @param connection the connection + * @param function the predicate + * @param data data to pass to the predicate + * @param free_data_function function to free the data + */ +void +dbus_connection_set_unix_user_function (DBusConnection *connection, + DBusAllowUnixUserFunction function, + void *data, + DBusFreeFunction free_data_function) +{ + void *old_data = NULL; + DBusFreeFunction old_free_function = NULL; + + _dbus_return_if_fail (connection != NULL); + + CONNECTION_LOCK (connection); + _dbus_transport_set_unix_user_function (connection->transport, + function, data, free_data_function, + &old_data, &old_free_function); + CONNECTION_UNLOCK (connection); + + if (old_free_function != NULL) + (* old_free_function) (old_data); +} + +/** + * Gets the Windows user SID of the connection if known. Returns + * #TRUE if the ID is filled in. Always returns #FALSE on non-Windows + * platforms for now, though in theory someone could hook UNIX to + * Active Directory or something. Always returns #FALSE prior to + * authenticating the connection. + * + * The user is only read by servers from clients; clients can't usually + * get the user of servers, because servers do not authenticate to + * clients. The returned user is the user the connection authenticated + * as. + * + * The message bus is a server and the apps connecting to the bus + * are clients. + * + * The returned user string has to be freed with dbus_free(). + * + * The return value indicates whether the user SID is available; + * if it's available but we don't have the memory to copy it, + * then the return value is #TRUE and #NULL is given as the SID. + * + * @todo We would like to be able to say "You can ask the bus to tell + * you the user of another connection though if you like; this is done + * with dbus_bus_get_windows_user()." But this has to be implemented + * in bus/driver.c and dbus/dbus-bus.c, and is pointless anyway + * since on Windows we only use the session bus for now. + * + * @param connection the connection + * @param windows_sid_p return location for an allocated copy of the user ID, or #NULL if no memory + * @returns #TRUE if user is available (returned value may be #NULL anyway if no memory) + */ +dbus_bool_t +dbus_connection_get_windows_user (DBusConnection *connection, + char **windows_sid_p) +{ + dbus_bool_t result; + + _dbus_return_val_if_fail (connection != NULL, FALSE); + _dbus_return_val_if_fail (windows_sid_p != NULL, FALSE); + + CONNECTION_LOCK (connection); + + if (!_dbus_transport_get_is_authenticated (connection->transport)) + result = FALSE; + else + result = _dbus_transport_get_windows_user (connection->transport, + windows_sid_p); + +#ifdef DBUS_UNIX + _dbus_assert (!result); +#endif + + CONNECTION_UNLOCK (connection); + + return result; +} + +/** + * Sets a predicate function used to determine whether a given user ID + * is allowed to connect. When an incoming connection has + * authenticated with a particular user ID, this function is called; + * if it returns #TRUE, the connection is allowed to proceed, + * otherwise the connection is disconnected. + * + * If the function is set to #NULL (as it is by default), then + * only the same user owning the server process will be allowed to + * connect. + * + * On UNIX, the function will be set and its free_data_function will + * be invoked when the connection is freed or a new function is set. + * However, the function will never be called, because there is no + * way right now to authenticate as a Windows user on UNIX. + * + * @param connection the connection + * @param function the predicate + * @param data data to pass to the predicate + * @param free_data_function function to free the data + */ +void +dbus_connection_set_windows_user_function (DBusConnection *connection, + DBusAllowWindowsUserFunction function, + void *data, + DBusFreeFunction free_data_function) +{ + void *old_data = NULL; + DBusFreeFunction old_free_function = NULL; + + _dbus_return_if_fail (connection != NULL); + + CONNECTION_LOCK (connection); + _dbus_transport_set_windows_user_function (connection->transport, + function, data, free_data_function, + &old_data, &old_free_function); + CONNECTION_UNLOCK (connection); + + if (old_free_function != NULL) + (* old_free_function) (old_data); +} + +/** + * This function must be called on the server side of a connection when the + * connection is first seen in the #DBusNewConnectionFunction. If set to + * #TRUE (the default is #FALSE), then the connection can proceed even if + * the client does not authenticate as some user identity, i.e. clients + * can connect anonymously. + * + * This setting interacts with the available authorization mechanisms + * (see dbus_server_set_auth_mechanisms()). Namely, an auth mechanism + * such as ANONYMOUS that supports anonymous auth must be included in + * the list of available mechanisms for anonymous login to work. + * + * This setting also changes the default rule for connections + * authorized as a user; normally, if a connection authorizes as + * a user identity, it is permitted if the user identity is + * root or the user identity matches the user identity of the server + * process. If anonymous connections are allowed, however, + * then any user identity is allowed. + * + * You can override the rules for connections authorized as a + * user identity with dbus_connection_set_unix_user_function() + * and dbus_connection_set_windows_user_function(). + * + * @param connection the connection + * @param value whether to allow authentication as an anonymous user + */ +void +dbus_connection_set_allow_anonymous (DBusConnection *connection, + dbus_bool_t value) +{ + _dbus_return_if_fail (connection != NULL); + + CONNECTION_LOCK (connection); + _dbus_transport_set_allow_anonymous (connection->transport, value); + CONNECTION_UNLOCK (connection); +} + +/** + * + * Normally #DBusConnection automatically handles all messages to the + * org.freedesktop.DBus.Peer interface. However, the message bus wants + * to be able to route methods on that interface through the bus and + * to other applications. If routing peer messages is enabled, then + * messages with the org.freedesktop.DBus.Peer interface that also + * have a bus destination name set will not be automatically + * handled by the #DBusConnection and instead will be dispatched + * normally to the application. + * + * If a normal application sets this flag, it can break things badly. + * So don't set this unless you are the message bus. + * + * @param connection the connection + * @param value #TRUE to pass through org.freedesktop.DBus.Peer messages with a bus name set + */ +void +dbus_connection_set_route_peer_messages (DBusConnection *connection, + dbus_bool_t value) +{ + _dbus_return_if_fail (connection != NULL); + + CONNECTION_LOCK (connection); + connection->route_peer_messages = TRUE; + CONNECTION_UNLOCK (connection); +} + +/** + * Adds a message filter. Filters are handlers that are run on all + * incoming messages, prior to the objects registered with + * dbus_connection_register_object_path(). Filters are run in the + * order that they were added. The same handler can be added as a + * filter more than once, in which case it will be run more than once. + * Filters added during a filter callback won't be run on the message + * being processed. + * + * @todo we don't run filters on messages while blocking without + * entering the main loop, since filters are run as part of + * dbus_connection_dispatch(). This is probably a feature, as filters + * could create arbitrary reentrancy. But kind of sucks if you're + * trying to filter METHOD_RETURN for some reason. + * + * @param connection the connection + * @param function function to handle messages + * @param user_data user data to pass to the function + * @param free_data_function function to use for freeing user data + * @returns #TRUE on success, #FALSE if not enough memory. + */ +dbus_bool_t +dbus_connection_add_filter (DBusConnection *connection, + DBusHandleMessageFunction function, + void *user_data, + DBusFreeFunction free_data_function) +{ + DBusMessageFilter *filter; + + _dbus_return_val_if_fail (connection != NULL, FALSE); + _dbus_return_val_if_fail (function != NULL, FALSE); + + filter = dbus_new0 (DBusMessageFilter, 1); + if (filter == NULL) + return FALSE; + + filter->refcount.value = 1; + + CONNECTION_LOCK (connection); + + if (!_dbus_list_append (&connection->filter_list, + filter)) + { + _dbus_message_filter_unref (filter); + CONNECTION_UNLOCK (connection); + return FALSE; + } + + /* Fill in filter after all memory allocated, + * so we don't run the free_user_data_function + * if the add_filter() fails + */ + + filter->function = function; + filter->user_data = user_data; + filter->free_user_data_function = free_data_function; + + CONNECTION_UNLOCK (connection); + return TRUE; +} + +/** + * Removes a previously-added message filter. It is a programming + * error to call this function for a handler that has not been added + * as a filter. If the given handler was added more than once, only + * one instance of it will be removed (the most recently-added + * instance). + * + * @param connection the connection + * @param function the handler to remove + * @param user_data user data for the handler to remove + * + */ +void +dbus_connection_remove_filter (DBusConnection *connection, + DBusHandleMessageFunction function, + void *user_data) +{ + DBusList *link; + DBusMessageFilter *filter; + + _dbus_return_if_fail (connection != NULL); + _dbus_return_if_fail (function != NULL); + + CONNECTION_LOCK (connection); + + filter = NULL; + + link = _dbus_list_get_last_link (&connection->filter_list); + while (link != NULL) + { + filter = link->data; + + if (filter->function == function && + filter->user_data == user_data) + { + _dbus_list_remove_link (&connection->filter_list, link); + filter->function = NULL; + + break; + } + + link = _dbus_list_get_prev_link (&connection->filter_list, link); + } + + CONNECTION_UNLOCK (connection); + +#ifndef DBUS_DISABLE_CHECKS + if (filter == NULL) + { + _dbus_warn_check_failed ("Attempt to remove filter function %p user data %p, but no such filter has been added\n", + function, user_data); + return; + } +#endif + + /* Call application code */ + if (filter->free_user_data_function) + (* filter->free_user_data_function) (filter->user_data); + + filter->free_user_data_function = NULL; + filter->user_data = NULL; + + _dbus_message_filter_unref (filter); +} + +/** + * Registers a handler for a given path in the object hierarchy. + * The given vtable handles messages sent to exactly the given path. + * + * @param connection the connection + * @param path a '/' delimited string of path elements + * @param vtable the virtual table + * @param user_data data to pass to functions in the vtable + * @param error address where an error can be returned + * @returns #FALSE if an error (#DBUS_ERROR_NO_MEMORY or + * #DBUS_ERROR_ADDRESS_IN_USE) is reported + */ +dbus_bool_t +dbus_connection_try_register_object_path (DBusConnection *connection, + const char *path, + const DBusObjectPathVTable *vtable, + void *user_data, + DBusError *error) +{ + char **decomposed_path; + dbus_bool_t retval; + + _dbus_return_val_if_fail (connection != NULL, FALSE); + _dbus_return_val_if_fail (path != NULL, FALSE); + _dbus_return_val_if_fail (path[0] == '/', FALSE); + _dbus_return_val_if_fail (vtable != NULL, FALSE); + + if (!_dbus_decompose_path (path, strlen (path), &decomposed_path, NULL)) + return FALSE; + + CONNECTION_LOCK (connection); + + retval = _dbus_object_tree_register (connection->objects, + FALSE, + (const char **) decomposed_path, vtable, + user_data, error); + + CONNECTION_UNLOCK (connection); + + dbus_free_string_array (decomposed_path); + + return retval; +} + +/** + * Registers a handler for a given path in the object hierarchy. + * The given vtable handles messages sent to exactly the given path. + * + * It is a bug to call this function for object paths which already + * have a handler. Use dbus_connection_try_register_object_path() if this + * might be the case. + * + * @param connection the connection + * @param path a '/' delimited string of path elements + * @param vtable the virtual table + * @param user_data data to pass to functions in the vtable + * @returns #FALSE if not enough memory + */ +dbus_bool_t +dbus_connection_register_object_path (DBusConnection *connection, + const char *path, + const DBusObjectPathVTable *vtable, + void *user_data) +{ + char **decomposed_path; + dbus_bool_t retval; + DBusError error = DBUS_ERROR_INIT; + + _dbus_return_val_if_fail (connection != NULL, FALSE); + _dbus_return_val_if_fail (path != NULL, FALSE); + _dbus_return_val_if_fail (path[0] == '/', FALSE); + _dbus_return_val_if_fail (vtable != NULL, FALSE); + + if (!_dbus_decompose_path (path, strlen (path), &decomposed_path, NULL)) + return FALSE; + + CONNECTION_LOCK (connection); + + retval = _dbus_object_tree_register (connection->objects, + FALSE, + (const char **) decomposed_path, vtable, + user_data, &error); + + CONNECTION_UNLOCK (connection); + + dbus_free_string_array (decomposed_path); + + if (dbus_error_has_name (&error, DBUS_ERROR_ADDRESS_IN_USE)) + { + _dbus_warn ("%s\n", error.message); + dbus_error_free (&error); + return FALSE; + } + + return retval; +} + +/** + * Registers a fallback handler for a given subsection of the object + * hierarchy. The given vtable handles messages at or below the given + * path. You can use this to establish a default message handling + * policy for a whole "subdirectory." + * + * @param connection the connection + * @param path a '/' delimited string of path elements + * @param vtable the virtual table + * @param user_data data to pass to functions in the vtable + * @param error address where an error can be returned + * @returns #FALSE if an error (#DBUS_ERROR_NO_MEMORY or + * #DBUS_ERROR_ADDRESS_IN_USE) is reported + */ +dbus_bool_t +dbus_connection_try_register_fallback (DBusConnection *connection, + const char *path, + const DBusObjectPathVTable *vtable, + void *user_data, + DBusError *error) +{ + char **decomposed_path; + dbus_bool_t retval; + + _dbus_return_val_if_fail (connection != NULL, FALSE); + _dbus_return_val_if_fail (path != NULL, FALSE); + _dbus_return_val_if_fail (path[0] == '/', FALSE); + _dbus_return_val_if_fail (vtable != NULL, FALSE); + + if (!_dbus_decompose_path (path, strlen (path), &decomposed_path, NULL)) + return FALSE; + + CONNECTION_LOCK (connection); + + retval = _dbus_object_tree_register (connection->objects, + TRUE, + (const char **) decomposed_path, vtable, + user_data, error); + + CONNECTION_UNLOCK (connection); + + dbus_free_string_array (decomposed_path); + + return retval; +} + +/** + * Registers a fallback handler for a given subsection of the object + * hierarchy. The given vtable handles messages at or below the given + * path. You can use this to establish a default message handling + * policy for a whole "subdirectory." + * + * It is a bug to call this function for object paths which already + * have a handler. Use dbus_connection_try_register_fallback() if this + * might be the case. + * + * @param connection the connection + * @param path a '/' delimited string of path elements + * @param vtable the virtual table + * @param user_data data to pass to functions in the vtable + * @returns #FALSE if not enough memory + */ +dbus_bool_t +dbus_connection_register_fallback (DBusConnection *connection, + const char *path, + const DBusObjectPathVTable *vtable, + void *user_data) +{ + char **decomposed_path; + dbus_bool_t retval; + DBusError error = DBUS_ERROR_INIT; + + _dbus_return_val_if_fail (connection != NULL, FALSE); + _dbus_return_val_if_fail (path != NULL, FALSE); + _dbus_return_val_if_fail (path[0] == '/', FALSE); + _dbus_return_val_if_fail (vtable != NULL, FALSE); + + if (!_dbus_decompose_path (path, strlen (path), &decomposed_path, NULL)) + return FALSE; + + CONNECTION_LOCK (connection); + + retval = _dbus_object_tree_register (connection->objects, + TRUE, + (const char **) decomposed_path, vtable, + user_data, &error); + + CONNECTION_UNLOCK (connection); + + dbus_free_string_array (decomposed_path); + + if (dbus_error_has_name (&error, DBUS_ERROR_ADDRESS_IN_USE)) + { + _dbus_warn ("%s\n", error.message); + dbus_error_free (&error); + return FALSE; + } + + return retval; +} + +/** + * Unregisters the handler registered with exactly the given path. + * It's a bug to call this function for a path that isn't registered. + * Can unregister both fallback paths and object paths. + * + * @param connection the connection + * @param path a '/' delimited string of path elements + * @returns #FALSE if not enough memory + */ +dbus_bool_t +dbus_connection_unregister_object_path (DBusConnection *connection, + const char *path) +{ + char **decomposed_path; + + _dbus_return_val_if_fail (connection != NULL, FALSE); + _dbus_return_val_if_fail (path != NULL, FALSE); + _dbus_return_val_if_fail (path[0] == '/', FALSE); + + if (!_dbus_decompose_path (path, strlen (path), &decomposed_path, NULL)) + return FALSE; + + CONNECTION_LOCK (connection); + + _dbus_object_tree_unregister_and_unlock (connection->objects, (const char **) decomposed_path); + + dbus_free_string_array (decomposed_path); + + return TRUE; +} + +/** + * Gets the user data passed to dbus_connection_register_object_path() + * or dbus_connection_register_fallback(). If nothing was registered + * at this path, the data is filled in with #NULL. + * + * @param connection the connection + * @param path the path you registered with + * @param data_p location to store the user data, or #NULL + * @returns #FALSE if not enough memory + */ +dbus_bool_t +dbus_connection_get_object_path_data (DBusConnection *connection, + const char *path, + void **data_p) +{ + char **decomposed_path; + + _dbus_return_val_if_fail (connection != NULL, FALSE); + _dbus_return_val_if_fail (path != NULL, FALSE); + _dbus_return_val_if_fail (data_p != NULL, FALSE); + + *data_p = NULL; + + if (!_dbus_decompose_path (path, strlen (path), &decomposed_path, NULL)) + return FALSE; + + CONNECTION_LOCK (connection); + + *data_p = _dbus_object_tree_get_user_data_unlocked (connection->objects, (const char**) decomposed_path); + + CONNECTION_UNLOCK (connection); + + dbus_free_string_array (decomposed_path); + + return TRUE; +} + +/** + * Lists the registered fallback handlers and object path handlers at + * the given parent_path. The returned array should be freed with + * dbus_free_string_array(). + * + * @param connection the connection + * @param parent_path the path to list the child handlers of + * @param child_entries returns #NULL-terminated array of children + * @returns #FALSE if no memory to allocate the child entries + */ +dbus_bool_t +dbus_connection_list_registered (DBusConnection *connection, + const char *parent_path, + char ***child_entries) +{ + char **decomposed_path; + dbus_bool_t retval; + _dbus_return_val_if_fail (connection != NULL, FALSE); + _dbus_return_val_if_fail (parent_path != NULL, FALSE); + _dbus_return_val_if_fail (parent_path[0] == '/', FALSE); + _dbus_return_val_if_fail (child_entries != NULL, FALSE); + + if (!_dbus_decompose_path (parent_path, strlen (parent_path), &decomposed_path, NULL)) + return FALSE; + + CONNECTION_LOCK (connection); + + retval = _dbus_object_tree_list_registered_and_unlock (connection->objects, + (const char **) decomposed_path, + child_entries); + dbus_free_string_array (decomposed_path); + + return retval; +} + +static DBusDataSlotAllocator slot_allocator; +_DBUS_DEFINE_GLOBAL_LOCK (connection_slots); + +/** + * Allocates an integer ID to be used for storing application-specific + * data on any DBusConnection. The allocated ID may then be used + * with dbus_connection_set_data() and dbus_connection_get_data(). + * The passed-in slot must be initialized to -1, and is filled in + * with the slot ID. If the passed-in slot is not -1, it's assumed + * to be already allocated, and its refcount is incremented. + * + * The allocated slot is global, i.e. all DBusConnection objects will + * have a slot with the given integer ID reserved. + * + * @param slot_p address of a global variable storing the slot + * @returns #FALSE on failure (no memory) + */ +dbus_bool_t +dbus_connection_allocate_data_slot (dbus_int32_t *slot_p) +{ + return _dbus_data_slot_allocator_alloc (&slot_allocator, + &_DBUS_LOCK_NAME (connection_slots), + slot_p); +} + +/** + * Deallocates a global ID for connection data slots. + * dbus_connection_get_data() and dbus_connection_set_data() may no + * longer be used with this slot. Existing data stored on existing + * DBusConnection objects will be freed when the connection is + * finalized, but may not be retrieved (and may only be replaced if + * someone else reallocates the slot). When the refcount on the + * passed-in slot reaches 0, it is set to -1. + * + * @param slot_p address storing the slot to deallocate + */ +void +dbus_connection_free_data_slot (dbus_int32_t *slot_p) +{ + _dbus_return_if_fail (*slot_p >= 0); + + _dbus_data_slot_allocator_free (&slot_allocator, slot_p); +} + +/** + * Stores a pointer on a DBusConnection, along + * with an optional function to be used for freeing + * the data when the data is set again, or when + * the connection is finalized. The slot number + * must have been allocated with dbus_connection_allocate_data_slot(). + * + * @param connection the connection + * @param slot the slot number + * @param data the data to store + * @param free_data_func finalizer function for the data + * @returns #TRUE if there was enough memory to store the data + */ +dbus_bool_t +dbus_connection_set_data (DBusConnection *connection, + dbus_int32_t slot, + void *data, + DBusFreeFunction free_data_func) +{ + DBusFreeFunction old_free_func; + void *old_data; + dbus_bool_t retval; + + _dbus_return_val_if_fail (connection != NULL, FALSE); + _dbus_return_val_if_fail (slot >= 0, FALSE); + + CONNECTION_LOCK (connection); + + retval = _dbus_data_slot_list_set (&slot_allocator, + &connection->slot_list, + slot, data, free_data_func, + &old_free_func, &old_data); + + CONNECTION_UNLOCK (connection); + + if (retval) + { + /* Do the actual free outside the connection lock */ + if (old_free_func) + (* old_free_func) (old_data); + } + + return retval; +} + +/** + * Retrieves data previously set with dbus_connection_set_data(). + * The slot must still be allocated (must not have been freed). + * + * @param connection the connection + * @param slot the slot to get data from + * @returns the data, or #NULL if not found + */ +void* +dbus_connection_get_data (DBusConnection *connection, + dbus_int32_t slot) +{ + void *res; + + _dbus_return_val_if_fail (connection != NULL, NULL); + + CONNECTION_LOCK (connection); + + res = _dbus_data_slot_list_get (&slot_allocator, + &connection->slot_list, + slot); + + CONNECTION_UNLOCK (connection); + + return res; +} + +/** + * This function sets a global flag for whether dbus_connection_new() + * will set SIGPIPE behavior to SIG_IGN. + * + * @param will_modify_sigpipe #TRUE to allow sigpipe to be set to SIG_IGN + */ +void +dbus_connection_set_change_sigpipe (dbus_bool_t will_modify_sigpipe) +{ + _dbus_modify_sigpipe = will_modify_sigpipe != FALSE; +} + +/** + * Specifies the maximum size message this connection is allowed to + * receive. Larger messages will result in disconnecting the + * connection. + * + * @param connection a #DBusConnection + * @param size maximum message size the connection can receive, in bytes + */ +void +dbus_connection_set_max_message_size (DBusConnection *connection, + long size) +{ + _dbus_return_if_fail (connection != NULL); + + CONNECTION_LOCK (connection); + _dbus_transport_set_max_message_size (connection->transport, + size); + CONNECTION_UNLOCK (connection); +} + +/** + * Gets the value set by dbus_connection_set_max_message_size(). + * + * @param connection the connection + * @returns the max size of a single message + */ +long +dbus_connection_get_max_message_size (DBusConnection *connection) +{ + long res; + + _dbus_return_val_if_fail (connection != NULL, 0); + + CONNECTION_LOCK (connection); + res = _dbus_transport_get_max_message_size (connection->transport); + CONNECTION_UNLOCK (connection); + return res; +} + +/** + * Sets the maximum total number of bytes that can be used for all messages + * received on this connection. Messages count toward the maximum until + * they are finalized. When the maximum is reached, the connection will + * not read more data until some messages are finalized. + * + * The semantics of the maximum are: if outstanding messages are + * already above the maximum, additional messages will not be read. + * The semantics are not: if the next message would cause us to exceed + * the maximum, we don't read it. The reason is that we don't know the + * size of a message until after we read it. + * + * Thus, the max live messages size can actually be exceeded + * by up to the maximum size of a single message. + * + * Also, if we read say 1024 bytes off the wire in a single read(), + * and that contains a half-dozen small messages, we may exceed the + * size max by that amount. But this should be inconsequential. + * + * This does imply that we can't call read() with a buffer larger + * than we're willing to exceed this limit by. + * + * @param connection the connection + * @param size the maximum size in bytes of all outstanding messages + */ +void +dbus_connection_set_max_received_size (DBusConnection *connection, + long size) +{ + _dbus_return_if_fail (connection != NULL); + + CONNECTION_LOCK (connection); + _dbus_transport_set_max_received_size (connection->transport, + size); + CONNECTION_UNLOCK (connection); +} + +/** + * Gets the value set by dbus_connection_set_max_received_size(). + * + * @param connection the connection + * @returns the max size of all live messages + */ +long +dbus_connection_get_max_received_size (DBusConnection *connection) +{ + long res; + + _dbus_return_val_if_fail (connection != NULL, 0); + + CONNECTION_LOCK (connection); + res = _dbus_transport_get_max_received_size (connection->transport); + CONNECTION_UNLOCK (connection); + return res; +} + +/** + * Gets the approximate size in bytes of all messages in the outgoing + * message queue. The size is approximate in that you shouldn't use + * it to decide how many bytes to read off the network or anything + * of that nature, as optimizations may choose to tell small white lies + * to avoid performance overhead. + * + * @param connection the connection + * @returns the number of bytes that have been queued up but not sent + */ +long +dbus_connection_get_outgoing_size (DBusConnection *connection) +{ + long res; + + _dbus_return_val_if_fail (connection != NULL, 0); + + CONNECTION_LOCK (connection); + res = _dbus_counter_get_value (connection->outgoing_counter); + CONNECTION_UNLOCK (connection); + return res; +} + +/** @} */ diff --git a/src/dbus/dbus-connection.h b/src/dbus/dbus-connection.h new file mode 100644 index 0000000..b8fd35f --- /dev/null +++ b/src/dbus/dbus-connection.h @@ -0,0 +1,406 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-connection.h DBusConnection object + * + * Copyright (C) 2002, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION) +#error "Only can be included directly, this file may disappear or change contents." +#endif + +#ifndef DBUS_CONNECTION_H +#define DBUS_CONNECTION_H + +#include +#include +#include +#include + +DBUS_BEGIN_DECLS + +/** + * @addtogroup DBusConnection + * @{ + */ + +/* documented in dbus-watch.c */ +typedef struct DBusWatch DBusWatch; +/* documented in dbus-timeout.c */ +typedef struct DBusTimeout DBusTimeout; +/** Opaque type representing preallocated resources so a message can be sent without further memory allocation. */ +typedef struct DBusPreallocatedSend DBusPreallocatedSend; +/** Opaque type representing a method call that has not yet received a reply. */ +typedef struct DBusPendingCall DBusPendingCall; +/** Opaque type representing a connection to a remote application and associated incoming/outgoing message queues. */ +typedef struct DBusConnection DBusConnection; +/** Set of functions that must be implemented to handle messages sent to a particular object path. */ +typedef struct DBusObjectPathVTable DBusObjectPathVTable; + +/** + * Indicates the status of a #DBusWatch. + */ +typedef enum +{ + DBUS_WATCH_READABLE = 1 << 0, /**< As in POLLIN */ + DBUS_WATCH_WRITABLE = 1 << 1, /**< As in POLLOUT */ + DBUS_WATCH_ERROR = 1 << 2, /**< As in POLLERR (can't watch for + * this, but can be present in + * current state passed to + * dbus_watch_handle()). + */ + DBUS_WATCH_HANGUP = 1 << 3 /**< As in POLLHUP (can't watch for + * it, but can be present in current + * state passed to + * dbus_watch_handle()). + */ +} DBusWatchFlags; + +/** + * Indicates the status of incoming data on a #DBusConnection. This determines whether + * dbus_connection_dispatch() needs to be called. + */ +typedef enum +{ + DBUS_DISPATCH_DATA_REMAINS, /**< There is more data to potentially convert to messages. */ + DBUS_DISPATCH_COMPLETE, /**< All currently available data has been processed. */ + DBUS_DISPATCH_NEED_MEMORY /**< More memory is needed to continue. */ +} DBusDispatchStatus; + +/** Called when libdbus needs a new watch to be monitored by the main + * loop. Returns #FALSE if it lacks enough memory to add the + * watch. Set by dbus_connection_set_watch_functions() or + * dbus_server_set_watch_functions(). + */ +typedef dbus_bool_t (* DBusAddWatchFunction) (DBusWatch *watch, + void *data); +/** Called when dbus_watch_get_enabled() may return a different value + * than it did before. Set by dbus_connection_set_watch_functions() + * or dbus_server_set_watch_functions(). + */ +typedef void (* DBusWatchToggledFunction) (DBusWatch *watch, + void *data); +/** Called when libdbus no longer needs a watch to be monitored by the + * main loop. Set by dbus_connection_set_watch_functions() or + * dbus_server_set_watch_functions(). + */ +typedef void (* DBusRemoveWatchFunction) (DBusWatch *watch, + void *data); +/** Called when libdbus needs a new timeout to be monitored by the main + * loop. Returns #FALSE if it lacks enough memory to add the + * watch. Set by dbus_connection_set_timeout_functions() or + * dbus_server_set_timeout_functions(). + */ +typedef dbus_bool_t (* DBusAddTimeoutFunction) (DBusTimeout *timeout, + void *data); +/** Called when dbus_timeout_get_enabled() may return a different + * value than it did before. + * Set by dbus_connection_set_timeout_functions() or + * dbus_server_set_timeout_functions(). + */ +typedef void (* DBusTimeoutToggledFunction) (DBusTimeout *timeout, + void *data); +/** Called when libdbus no longer needs a timeout to be monitored by the + * main loop. Set by dbus_connection_set_timeout_functions() or + * dbus_server_set_timeout_functions(). + */ +typedef void (* DBusRemoveTimeoutFunction) (DBusTimeout *timeout, + void *data); +/** Called when the return value of dbus_connection_get_dispatch_status() + * may have changed. Set with dbus_connection_set_dispatch_status_function(). + */ +typedef void (* DBusDispatchStatusFunction) (DBusConnection *connection, + DBusDispatchStatus new_status, + void *data); +/** + * Called when the main loop's thread should be notified that there's now work + * to do. Set with dbus_connection_set_wakeup_main_function(). + */ +typedef void (* DBusWakeupMainFunction) (void *data); + +/** + * Called during authentication to check whether the given UNIX user + * ID is allowed to connect, if the client tried to auth as a UNIX + * user ID. Normally on Windows this would never happen. Set with + * dbus_connection_set_unix_user_function(). + */ +typedef dbus_bool_t (* DBusAllowUnixUserFunction) (DBusConnection *connection, + unsigned long uid, + void *data); + +/** + * Called during authentication to check whether the given Windows user + * ID is allowed to connect, if the client tried to auth as a Windows + * user ID. Normally on UNIX this would never happen. Set with + * dbus_connection_set_windows_user_function(). + */ +typedef dbus_bool_t (* DBusAllowWindowsUserFunction) (DBusConnection *connection, + const char *user_sid, + void *data); + + +/** + * Called when a pending call now has a reply available. Set with + * dbus_pending_call_set_notify(). + */ +typedef void (* DBusPendingCallNotifyFunction) (DBusPendingCall *pending, + void *user_data); + +/** + * Called when a message needs to be handled. The result indicates whether or + * not more handlers should be run. Set with dbus_connection_add_filter(). + */ +typedef DBusHandlerResult (* DBusHandleMessageFunction) (DBusConnection *connection, + DBusMessage *message, + void *user_data); + +DBusConnection* dbus_connection_open (const char *address, + DBusError *error); +DBusConnection* dbus_connection_open_private (const char *address, + DBusError *error); +DBusConnection* dbus_connection_ref (DBusConnection *connection); +void dbus_connection_unref (DBusConnection *connection); +void dbus_connection_close (DBusConnection *connection); +dbus_bool_t dbus_connection_get_is_connected (DBusConnection *connection); +dbus_bool_t dbus_connection_get_is_authenticated (DBusConnection *connection); +dbus_bool_t dbus_connection_get_is_anonymous (DBusConnection *connection); +char* dbus_connection_get_server_id (DBusConnection *connection); +void dbus_connection_set_exit_on_disconnect (DBusConnection *connection, + dbus_bool_t exit_on_disconnect); +void dbus_connection_flush (DBusConnection *connection); +dbus_bool_t dbus_connection_read_write_dispatch (DBusConnection *connection, + int timeout_milliseconds); +dbus_bool_t dbus_connection_read_write (DBusConnection *connection, + int timeout_milliseconds); +DBusMessage* dbus_connection_borrow_message (DBusConnection *connection); +void dbus_connection_return_message (DBusConnection *connection, + DBusMessage *message); +void dbus_connection_steal_borrowed_message (DBusConnection *connection, + DBusMessage *message); +DBusMessage* dbus_connection_pop_message (DBusConnection *connection); +DBusDispatchStatus dbus_connection_get_dispatch_status (DBusConnection *connection); +DBusDispatchStatus dbus_connection_dispatch (DBusConnection *connection); +dbus_bool_t dbus_connection_has_messages_to_send (DBusConnection *connection); +dbus_bool_t dbus_connection_send (DBusConnection *connection, + DBusMessage *message, + dbus_uint32_t *client_serial); +dbus_bool_t dbus_connection_send_with_reply (DBusConnection *connection, + DBusMessage *message, + DBusPendingCall **pending_return, + int timeout_milliseconds); +DBusMessage * dbus_connection_send_with_reply_and_block (DBusConnection *connection, + DBusMessage *message, + int timeout_milliseconds, + DBusError *error); +dbus_bool_t dbus_connection_set_watch_functions (DBusConnection *connection, + DBusAddWatchFunction add_function, + DBusRemoveWatchFunction remove_function, + DBusWatchToggledFunction toggled_function, + void *data, + DBusFreeFunction free_data_function); +dbus_bool_t dbus_connection_set_timeout_functions (DBusConnection *connection, + DBusAddTimeoutFunction add_function, + DBusRemoveTimeoutFunction remove_function, + DBusTimeoutToggledFunction toggled_function, + void *data, + DBusFreeFunction free_data_function); +void dbus_connection_set_wakeup_main_function (DBusConnection *connection, + DBusWakeupMainFunction wakeup_main_function, + void *data, + DBusFreeFunction free_data_function); +void dbus_connection_set_dispatch_status_function (DBusConnection *connection, + DBusDispatchStatusFunction function, + void *data, + DBusFreeFunction free_data_function); +dbus_bool_t dbus_connection_get_unix_user (DBusConnection *connection, + unsigned long *uid); +dbus_bool_t dbus_connection_get_unix_process_id (DBusConnection *connection, + unsigned long *pid); +dbus_bool_t dbus_connection_get_adt_audit_session_data (DBusConnection *connection, + void **data, + dbus_int32_t *data_size); +void dbus_connection_set_unix_user_function (DBusConnection *connection, + DBusAllowUnixUserFunction function, + void *data, + DBusFreeFunction free_data_function); +dbus_bool_t dbus_connection_get_windows_user (DBusConnection *connection, + char **windows_sid_p); +void dbus_connection_set_windows_user_function (DBusConnection *connection, + DBusAllowWindowsUserFunction function, + void *data, + DBusFreeFunction free_data_function); +void dbus_connection_set_allow_anonymous (DBusConnection *connection, + dbus_bool_t value); +void dbus_connection_set_route_peer_messages (DBusConnection *connection, + dbus_bool_t value); + + +/* Filters */ + +dbus_bool_t dbus_connection_add_filter (DBusConnection *connection, + DBusHandleMessageFunction function, + void *user_data, + DBusFreeFunction free_data_function); +void dbus_connection_remove_filter (DBusConnection *connection, + DBusHandleMessageFunction function, + void *user_data); + + +/* Other */ +dbus_bool_t dbus_connection_allocate_data_slot (dbus_int32_t *slot_p); +void dbus_connection_free_data_slot (dbus_int32_t *slot_p); +dbus_bool_t dbus_connection_set_data (DBusConnection *connection, + dbus_int32_t slot, + void *data, + DBusFreeFunction free_data_func); +void* dbus_connection_get_data (DBusConnection *connection, + dbus_int32_t slot); + +void dbus_connection_set_change_sigpipe (dbus_bool_t will_modify_sigpipe); + +void dbus_connection_set_max_message_size (DBusConnection *connection, + long size); +long dbus_connection_get_max_message_size (DBusConnection *connection); +void dbus_connection_set_max_received_size (DBusConnection *connection, + long size); +long dbus_connection_get_max_received_size (DBusConnection *connection); +long dbus_connection_get_outgoing_size (DBusConnection *connection); + +DBusPreallocatedSend* dbus_connection_preallocate_send (DBusConnection *connection); +void dbus_connection_free_preallocated_send (DBusConnection *connection, + DBusPreallocatedSend *preallocated); +void dbus_connection_send_preallocated (DBusConnection *connection, + DBusPreallocatedSend *preallocated, + DBusMessage *message, + dbus_uint32_t *client_serial); + + +/* Object tree functionality */ + +/** + * Called when a #DBusObjectPathVTable is unregistered (or its connection is freed). + * Found in #DBusObjectPathVTable. + */ +typedef void (* DBusObjectPathUnregisterFunction) (DBusConnection *connection, + void *user_data); +/** + * Called when a message is sent to a registered object path. Found in + * #DBusObjectPathVTable which is registered with dbus_connection_register_object_path() + * or dbus_connection_register_fallback(). + */ +typedef DBusHandlerResult (* DBusObjectPathMessageFunction) (DBusConnection *connection, + DBusMessage *message, + void *user_data); + +/** + * Virtual table that must be implemented to handle a portion of the + * object path hierarchy. Attach the vtable to a particular path using + * dbus_connection_register_object_path() or + * dbus_connection_register_fallback(). + */ +struct DBusObjectPathVTable +{ + DBusObjectPathUnregisterFunction unregister_function; /**< Function to unregister this handler */ + DBusObjectPathMessageFunction message_function; /**< Function to handle messages */ + + void (* dbus_internal_pad1) (void *); /**< Reserved for future expansion */ + void (* dbus_internal_pad2) (void *); /**< Reserved for future expansion */ + void (* dbus_internal_pad3) (void *); /**< Reserved for future expansion */ + void (* dbus_internal_pad4) (void *); /**< Reserved for future expansion */ +}; + +dbus_bool_t dbus_connection_try_register_object_path (DBusConnection *connection, + const char *path, + const DBusObjectPathVTable *vtable, + void *user_data, + DBusError *error); + +dbus_bool_t dbus_connection_register_object_path (DBusConnection *connection, + const char *path, + const DBusObjectPathVTable *vtable, + void *user_data); + +dbus_bool_t dbus_connection_try_register_fallback (DBusConnection *connection, + const char *path, + const DBusObjectPathVTable *vtable, + void *user_data, + DBusError *error); + +dbus_bool_t dbus_connection_register_fallback (DBusConnection *connection, + const char *path, + const DBusObjectPathVTable *vtable, + void *user_data); +dbus_bool_t dbus_connection_unregister_object_path (DBusConnection *connection, + const char *path); + +dbus_bool_t dbus_connection_get_object_path_data (DBusConnection *connection, + const char *path, + void **data_p); + +dbus_bool_t dbus_connection_list_registered (DBusConnection *connection, + const char *parent_path, + char ***child_entries); + +dbus_bool_t dbus_connection_get_unix_fd (DBusConnection *connection, + int *fd); +dbus_bool_t dbus_connection_get_socket (DBusConnection *connection, + int *fd); + +/** @} */ + + +/** + * @addtogroup DBusWatch + * @{ + */ + +#ifndef DBUS_DISABLE_DEPRECATED +DBUS_DEPRECATED int dbus_watch_get_fd (DBusWatch *watch); +#endif + +int dbus_watch_get_unix_fd (DBusWatch *watch); +int dbus_watch_get_socket (DBusWatch *watch); +unsigned int dbus_watch_get_flags (DBusWatch *watch); +void* dbus_watch_get_data (DBusWatch *watch); +void dbus_watch_set_data (DBusWatch *watch, + void *data, + DBusFreeFunction free_data_function); +dbus_bool_t dbus_watch_handle (DBusWatch *watch, + unsigned int flags); +dbus_bool_t dbus_watch_get_enabled (DBusWatch *watch); + +/** @} */ + +/** + * @addtogroup DBusTimeout + * @{ + */ + +int dbus_timeout_get_interval (DBusTimeout *timeout); +void* dbus_timeout_get_data (DBusTimeout *timeout); +void dbus_timeout_set_data (DBusTimeout *timeout, + void *data, + DBusFreeFunction free_data_function); +dbus_bool_t dbus_timeout_handle (DBusTimeout *timeout); +dbus_bool_t dbus_timeout_get_enabled (DBusTimeout *timeout); + +/** @} */ + +DBUS_END_DECLS + +#endif /* DBUS_CONNECTION_H */ diff --git a/src/dbus/dbus-credentials-util.c b/src/dbus/dbus-credentials-util.c new file mode 100644 index 0000000..7430597 --- /dev/null +++ b/src/dbus/dbus-credentials-util.c @@ -0,0 +1,204 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-credentials-util.c Would be in dbus-credentials.c, but only used for tests/bus + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include "dbus-internals.h" +#include "dbus-test.h" +#include "dbus-credentials.h" + +/** + * @addtogroup DBusCredentials + * @{ + */ + +/** @} */ + +#ifdef DBUS_BUILD_TESTS +#include "dbus-test.h" +#include +#include + +static DBusCredentials* +make_credentials(dbus_uid_t unix_uid, + dbus_pid_t unix_pid, + const char *windows_sid) +{ + DBusCredentials *credentials; + + credentials = _dbus_credentials_new (); + + if (unix_uid != DBUS_UID_UNSET) + { + if (!_dbus_credentials_add_unix_uid (credentials, unix_uid)) + { + _dbus_credentials_unref (credentials); + return NULL; + } + } + + if (unix_pid != DBUS_PID_UNSET) + { + if (!_dbus_credentials_add_unix_pid (credentials, unix_pid)) + { + _dbus_credentials_unref (credentials); + return NULL; + } + } + + if (windows_sid != NULL) + { + if (!_dbus_credentials_add_windows_sid (credentials, windows_sid)) + { + _dbus_credentials_unref (credentials); + return NULL; + } + } + + return credentials; +} + +#define SAMPLE_SID "whatever a windows sid looks like" +#define OTHER_SAMPLE_SID "whatever else" + +dbus_bool_t +_dbus_credentials_test (const char *test_data_dir) +{ + DBusCredentials *creds; + DBusCredentials *creds2; + + if (test_data_dir == NULL) + return TRUE; + + creds = make_credentials (12, 511, SAMPLE_SID); + if (creds == NULL) + _dbus_assert_not_reached ("oom"); + + /* test refcounting */ + _dbus_credentials_ref (creds); + _dbus_credentials_unref (creds); + + _dbus_assert (_dbus_credentials_include (creds, DBUS_CREDENTIAL_UNIX_USER_ID)); + _dbus_assert (_dbus_credentials_include (creds, DBUS_CREDENTIAL_UNIX_PROCESS_ID)); + _dbus_assert (_dbus_credentials_include (creds, DBUS_CREDENTIAL_WINDOWS_SID)); + + _dbus_assert (_dbus_credentials_get_unix_uid (creds) == 12); + _dbus_assert (_dbus_credentials_get_unix_pid (creds) == 511); + _dbus_assert (strcmp (_dbus_credentials_get_windows_sid (creds), SAMPLE_SID) == 0); + + _dbus_assert (!_dbus_credentials_are_empty (creds)); + _dbus_assert (!_dbus_credentials_are_anonymous (creds)); + + /* Test copy */ + creds2 = _dbus_credentials_copy (creds); + if (creds2 == NULL) + _dbus_assert_not_reached ("oom"); + + _dbus_assert (_dbus_credentials_include (creds2, DBUS_CREDENTIAL_UNIX_USER_ID)); + _dbus_assert (_dbus_credentials_include (creds2, DBUS_CREDENTIAL_UNIX_PROCESS_ID)); + _dbus_assert (_dbus_credentials_include (creds2, DBUS_CREDENTIAL_WINDOWS_SID)); + + _dbus_assert (_dbus_credentials_get_unix_uid (creds2) == 12); + _dbus_assert (_dbus_credentials_get_unix_pid (creds2) == 511); + _dbus_assert (strcmp (_dbus_credentials_get_windows_sid (creds2), SAMPLE_SID) == 0); + + _dbus_assert (_dbus_credentials_are_superset (creds, creds2)); + + _dbus_credentials_unref (creds2); + + /* Same user if both unix and windows are the same */ + creds2 = make_credentials (12, DBUS_PID_UNSET, SAMPLE_SID); + if (creds2 == NULL) + _dbus_assert_not_reached ("oom"); + + _dbus_assert (_dbus_credentials_same_user (creds, creds2)); + + _dbus_credentials_unref (creds2); + + /* Not the same user if Windows is missing */ + creds2 = make_credentials (12, DBUS_PID_UNSET, NULL); + if (creds2 == NULL) + _dbus_assert_not_reached ("oom"); + + _dbus_assert (!_dbus_credentials_same_user (creds, creds2)); + _dbus_assert (_dbus_credentials_are_superset (creds, creds2)); + + _dbus_credentials_unref (creds2); + + /* Not the same user if Windows is different */ + creds2 = make_credentials (12, DBUS_PID_UNSET, OTHER_SAMPLE_SID); + if (creds2 == NULL) + _dbus_assert_not_reached ("oom"); + + _dbus_assert (!_dbus_credentials_same_user (creds, creds2)); + _dbus_assert (!_dbus_credentials_are_superset (creds, creds2)); + + _dbus_credentials_unref (creds2); + + /* Not the same user if Unix is missing */ + creds2 = make_credentials (DBUS_UID_UNSET, DBUS_PID_UNSET, SAMPLE_SID); + if (creds2 == NULL) + _dbus_assert_not_reached ("oom"); + + _dbus_assert (!_dbus_credentials_same_user (creds, creds2)); + _dbus_assert (_dbus_credentials_are_superset (creds, creds2)); + + _dbus_credentials_unref (creds2); + + /* Not the same user if Unix is different */ + creds2 = make_credentials (15, DBUS_PID_UNSET, SAMPLE_SID); + if (creds2 == NULL) + _dbus_assert_not_reached ("oom"); + + _dbus_assert (!_dbus_credentials_same_user (creds, creds2)); + _dbus_assert (!_dbus_credentials_are_superset (creds, creds2)); + + _dbus_credentials_unref (creds2); + + /* Not the same user if both are missing */ + creds2 = make_credentials (DBUS_UID_UNSET, DBUS_PID_UNSET, NULL); + if (creds2 == NULL) + _dbus_assert_not_reached ("oom"); + + _dbus_assert (!_dbus_credentials_same_user (creds, creds2)); + _dbus_assert (_dbus_credentials_are_superset (creds, creds2)); + + _dbus_credentials_unref (creds2); + + /* Clearing credentials works */ + _dbus_credentials_clear (creds); + + _dbus_assert (!_dbus_credentials_include (creds, DBUS_CREDENTIAL_UNIX_USER_ID)); + _dbus_assert (!_dbus_credentials_include (creds, DBUS_CREDENTIAL_UNIX_PROCESS_ID)); + _dbus_assert (!_dbus_credentials_include (creds, DBUS_CREDENTIAL_WINDOWS_SID)); + + _dbus_assert (_dbus_credentials_get_unix_uid (creds) == DBUS_UID_UNSET); + _dbus_assert (_dbus_credentials_get_unix_pid (creds) == DBUS_PID_UNSET); + _dbus_assert (_dbus_credentials_get_windows_sid (creds) == NULL); + + _dbus_assert (_dbus_credentials_are_empty (creds)); + _dbus_assert (_dbus_credentials_are_anonymous (creds)); + + _dbus_credentials_unref (creds); + + return TRUE; +} + +#endif /* DBUS_BUILD_TESTS */ diff --git a/src/dbus/dbus-credentials.c b/src/dbus/dbus-credentials.c new file mode 100644 index 0000000..f21ecb2 --- /dev/null +++ b/src/dbus/dbus-credentials.c @@ -0,0 +1,507 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-credentials.c Credentials provable through authentication + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include +#include +#include "dbus-credentials.h" +#include "dbus-internals.h" + +/** + * @defgroup DBusCredentials Credentials provable through authentication + * @ingroup DBusInternals + * @brief DBusCredentials object + * + * Credentials are what you have to prove you have in order to + * authenticate. The main credentials right now are a unix user + * account, a Windows user account, or a UNIX process ID. + */ + +/** + * @defgroup DBusCredentialsInternals Credentials implementation details + * @ingroup DBusInternals + * @brief DBusCredentials implementation details + * + * Private details of credentials code. + * + * @{ + */ + +struct DBusCredentials { + int refcount; + dbus_uid_t unix_uid; + dbus_pid_t unix_pid; + char *windows_sid; + void *adt_audit_data; + dbus_int32_t adt_audit_data_size; +}; + +/** @} */ + +/** + * @addtogroup DBusCredentials + * @{ + */ + +/** + * Creates a new credentials object. + * + * @returns the new object or #NULL if no memory + */ +DBusCredentials* +_dbus_credentials_new (void) +{ + DBusCredentials *creds; + + creds = dbus_new (DBusCredentials, 1); + if (creds == NULL) + return NULL; + + creds->refcount = 1; + creds->unix_uid = DBUS_UID_UNSET; + creds->unix_pid = DBUS_PID_UNSET; + creds->windows_sid = NULL; + creds->adt_audit_data = NULL; + creds->adt_audit_data_size = 0; + + return creds; +} + +/** + * Creates a new object with credentials (user ID and process ID) from the current process. + * @returns the new object or #NULL if no memory + */ +DBusCredentials* +_dbus_credentials_new_from_current_process (void) +{ + DBusCredentials *creds; + + creds = _dbus_credentials_new (); + if (creds == NULL) + return NULL; + + if (!_dbus_credentials_add_from_current_process (creds)) + { + _dbus_credentials_unref (creds); + return NULL; + } + + return creds; +} + +/** + * Increment refcount on credentials. + * + * @param credentials the object + */ +void +_dbus_credentials_ref (DBusCredentials *credentials) +{ + _dbus_assert (credentials->refcount > 0); + credentials->refcount += 1; +} + +/** + * Decrement refcount on credentials. + * + * @param credentials the object + */ +void +_dbus_credentials_unref (DBusCredentials *credentials) +{ + _dbus_assert (credentials->refcount > 0); + + credentials->refcount -= 1; + if (credentials->refcount == 0) + { + dbus_free (credentials->windows_sid); + dbus_free (credentials->adt_audit_data); + dbus_free (credentials); + } +} + +/** + * Add a UNIX process ID to the credentials. + * + * @param credentials the object + * @param pid the process ID + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_credentials_add_unix_pid (DBusCredentials *credentials, + dbus_pid_t pid) +{ + credentials->unix_pid = pid; + return TRUE; +} + +/** + * Add a UNIX user ID to the credentials. + * + * @param credentials the object + * @param uid the user ID + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_credentials_add_unix_uid(DBusCredentials *credentials, + dbus_uid_t uid) +{ + credentials->unix_uid = uid; + return TRUE; + +} + +/** + * Add a Windows user SID to the credentials. + * + * @param credentials the object + * @param windows_sid the user SID + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_credentials_add_windows_sid (DBusCredentials *credentials, + const char *windows_sid) +{ + char *copy; + + copy = _dbus_strdup (windows_sid); + if (copy == NULL) + return FALSE; + + dbus_free (credentials->windows_sid); + credentials->windows_sid = copy; + + return TRUE; +} + +/** + * Add ADT audit data to the credentials. + * + * @param credentials the object + * @param audit_data the audit data + * @param size the length of audit data + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_credentials_add_adt_audit_data (DBusCredentials *credentials, + void *audit_data, + dbus_int32_t size) +{ + void *copy; + copy = _dbus_memdup (audit_data, size); + if (copy == NULL) + return FALSE; + + dbus_free (credentials->adt_audit_data); + credentials->adt_audit_data = copy; + credentials->adt_audit_data_size = size; + + return TRUE; +} + +/** + * Checks whether the given credential is present. + * + * @param credentials the object + * @param type the credential to check for + * @returns #TRUE if the credential is present + */ +dbus_bool_t +_dbus_credentials_include (DBusCredentials *credentials, + DBusCredentialType type) +{ + switch (type) + { + case DBUS_CREDENTIAL_UNIX_PROCESS_ID: + return credentials->unix_pid != DBUS_PID_UNSET; + case DBUS_CREDENTIAL_UNIX_USER_ID: + return credentials->unix_uid != DBUS_UID_UNSET; + case DBUS_CREDENTIAL_WINDOWS_SID: + return credentials->windows_sid != NULL; + case DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID: + return credentials->adt_audit_data != NULL; + } + + _dbus_assert_not_reached ("Unknown credential enum value"); + return FALSE; +} + +/** + * Gets the UNIX process ID in the credentials, or #DBUS_PID_UNSET if + * the credentials object doesn't contain a process ID. + * + * @param credentials the object + * @returns UNIX process ID + */ +dbus_pid_t +_dbus_credentials_get_unix_pid (DBusCredentials *credentials) +{ + return credentials->unix_pid; +} + +/** + * Gets the UNIX user ID in the credentials, or #DBUS_UID_UNSET if + * the credentials object doesn't contain a user ID. + * + * @param credentials the object + * @returns UNIX user ID + */ +dbus_uid_t +_dbus_credentials_get_unix_uid (DBusCredentials *credentials) +{ + return credentials->unix_uid; +} + +/** + * Gets the Windows user SID in the credentials, or #NULL if + * the credentials object doesn't contain a Windows user SID. + * + * @param credentials the object + * @returns Windows user SID + */ +const char* +_dbus_credentials_get_windows_sid (DBusCredentials *credentials) +{ + return credentials->windows_sid; +} + +/** + * Gets the ADT audit data in the credentials, or #NULL if + * the credentials object doesn't contain ADT audit data. + * + * @param credentials the object + * @returns Solaris ADT audit data + */ +void * +_dbus_credentials_get_adt_audit_data (DBusCredentials *credentials) +{ + return credentials->adt_audit_data; +} + +/** + * Gets the ADT audit data size in the credentials, or 0 if + * the credentials object doesn't contain ADT audit data. + * + * @param credentials the object + * @returns Solaris ADT audit data size + */ +dbus_int32_t +_dbus_credentials_get_adt_audit_data_size (DBusCredentials *credentials) +{ + return credentials->adt_audit_data_size; +} + +/** + * Checks whether the first credentials object contains + * all the credentials found in the second credentials object. + * + * @param credentials the object + * @param possible_subset see if credentials in here are also in the first arg + * @returns #TRUE if second arg is contained in first + */ +dbus_bool_t +_dbus_credentials_are_superset (DBusCredentials *credentials, + DBusCredentials *possible_subset) +{ + return + (possible_subset->unix_pid == DBUS_PID_UNSET || + possible_subset->unix_pid == credentials->unix_pid) && + (possible_subset->unix_uid == DBUS_UID_UNSET || + possible_subset->unix_uid == credentials->unix_uid) && + (possible_subset->windows_sid == NULL || + (credentials->windows_sid && strcmp (possible_subset->windows_sid, + credentials->windows_sid) == 0)) && + (possible_subset->adt_audit_data == NULL || + (credentials->adt_audit_data && memcmp (possible_subset->adt_audit_data, + credentials->adt_audit_data, + credentials->adt_audit_data_size) == 0)); +} + +/** + * Checks whether a credentials object contains anything. + * + * @param credentials the object + * @returns #TRUE if there are no credentials in the object + */ +dbus_bool_t +_dbus_credentials_are_empty (DBusCredentials *credentials) +{ + return + credentials->unix_pid == DBUS_PID_UNSET && + credentials->unix_uid == DBUS_UID_UNSET && + credentials->windows_sid == NULL && + credentials->adt_audit_data == NULL; +} + +/** + * Checks whether a credentials object contains a user identity. + * + * @param credentials the object + * @returns #TRUE if there are no user identities in the object + */ +dbus_bool_t +_dbus_credentials_are_anonymous (DBusCredentials *credentials) +{ + return + credentials->unix_uid == DBUS_UID_UNSET && + credentials->windows_sid == NULL; +} + +/** + * Merge all credentials found in the second object into the first object, + * overwriting the first object if there are any overlaps. + * + * @param credentials the object + * @param other_credentials credentials to merge + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_credentials_add_credentials (DBusCredentials *credentials, + DBusCredentials *other_credentials) +{ + return + _dbus_credentials_add_credential (credentials, + DBUS_CREDENTIAL_UNIX_PROCESS_ID, + other_credentials) && + _dbus_credentials_add_credential (credentials, + DBUS_CREDENTIAL_UNIX_USER_ID, + other_credentials) && + _dbus_credentials_add_credential (credentials, + DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID, + other_credentials) && + _dbus_credentials_add_credential (credentials, + DBUS_CREDENTIAL_WINDOWS_SID, + other_credentials); +} + +/** + * Merge the given credential found in the second object into the first object, + * overwriting the first object's value for that credential. + * + * Does nothing if the second object does not contain the specified credential. + * i.e., will never delete a credential from the first object. + * + * @param credentials the object + * @param which the credential to overwrite + * @param other_credentials credentials to merge + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_credentials_add_credential (DBusCredentials *credentials, + DBusCredentialType which, + DBusCredentials *other_credentials) +{ + if (which == DBUS_CREDENTIAL_UNIX_PROCESS_ID && + other_credentials->unix_pid != DBUS_PID_UNSET) + { + if (!_dbus_credentials_add_unix_pid (credentials, other_credentials->unix_pid)) + return FALSE; + } + else if (which == DBUS_CREDENTIAL_UNIX_USER_ID && + other_credentials->unix_uid != DBUS_UID_UNSET) + { + if (!_dbus_credentials_add_unix_uid (credentials, other_credentials->unix_uid)) + return FALSE; + } + else if (which == DBUS_CREDENTIAL_WINDOWS_SID && + other_credentials->windows_sid != NULL) + { + if (!_dbus_credentials_add_windows_sid (credentials, other_credentials->windows_sid)) + return FALSE; + } + else if (which == DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID && + other_credentials->adt_audit_data != NULL) + { + if (!_dbus_credentials_add_adt_audit_data (credentials, other_credentials->adt_audit_data, other_credentials->adt_audit_data_size)) + return FALSE; + } + + return TRUE; +} + +/** + * Clear all credentials in the object. + * + * @param credentials the object + */ +void +_dbus_credentials_clear (DBusCredentials *credentials) +{ + credentials->unix_pid = DBUS_PID_UNSET; + credentials->unix_uid = DBUS_UID_UNSET; + dbus_free (credentials->windows_sid); + credentials->windows_sid = NULL; + dbus_free (credentials->adt_audit_data); + credentials->adt_audit_data = NULL; + credentials->adt_audit_data_size = 0; +} + +/** + * Copy a credentials object. + * + * @param credentials the object + * @returns the copy or #NULL + */ +DBusCredentials* +_dbus_credentials_copy (DBusCredentials *credentials) +{ + DBusCredentials *copy; + + copy = _dbus_credentials_new (); + if (copy == NULL) + return NULL; + + if (!_dbus_credentials_add_credentials (copy, credentials)) + { + _dbus_credentials_unref (copy); + return NULL; + } + + return copy; +} + +/** + * Check whether the user-identifying credentials in two credentials + * objects are identical. Credentials that are not related to the + * user are ignored, but any kind of user ID credentials must be the + * same (UNIX user ID, Windows user SID, etc.) and present in both + * objects for the function to return #TRUE. + * + * @param credentials the object + * @param other_credentials credentials to compare + * @returns #TRUE if the two credentials refer to the same user + */ +dbus_bool_t +_dbus_credentials_same_user (DBusCredentials *credentials, + DBusCredentials *other_credentials) +{ + /* both windows and unix user must be the same (though pretty much + * in all conceivable cases, one will be unset) + */ + return credentials->unix_uid == other_credentials->unix_uid && + ((!(credentials->windows_sid || other_credentials->windows_sid)) || + (credentials->windows_sid && other_credentials->windows_sid && + strcmp (credentials->windows_sid, other_credentials->windows_sid) == 0)); +} + +/** @} */ + +/* tests in dbus-credentials-util.c */ diff --git a/src/dbus/dbus-credentials.h b/src/dbus/dbus-credentials.h new file mode 100644 index 0000000..8eed2bd --- /dev/null +++ b/src/dbus/dbus-credentials.h @@ -0,0 +1,78 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-credentials.h Credentials provable through authentication + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef DBUS_CREDENTIALS_H +#define DBUS_CREDENTIALS_H + +#include +#include +#include +#include + +DBUS_BEGIN_DECLS + +typedef enum { + DBUS_CREDENTIAL_UNIX_PROCESS_ID, + DBUS_CREDENTIAL_UNIX_USER_ID, + DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID, + DBUS_CREDENTIAL_WINDOWS_SID +} DBusCredentialType; + +DBusCredentials* _dbus_credentials_new_from_current_process (void); +DBusCredentials* _dbus_credentials_new (void); +void _dbus_credentials_ref (DBusCredentials *credentials); +void _dbus_credentials_unref (DBusCredentials *credentials); +dbus_bool_t _dbus_credentials_add_unix_pid (DBusCredentials *credentials, + dbus_pid_t pid); +dbus_bool_t _dbus_credentials_add_unix_uid (DBusCredentials *credentials, + dbus_uid_t uid); +dbus_bool_t _dbus_credentials_add_windows_sid (DBusCredentials *credentials, + const char *windows_sid); +dbus_bool_t _dbus_credentials_add_adt_audit_data (DBusCredentials *credentials, + void *audit_data, + dbus_int32_t size); +dbus_bool_t _dbus_credentials_include (DBusCredentials *credentials, + DBusCredentialType type); +dbus_pid_t _dbus_credentials_get_unix_pid (DBusCredentials *credentials); +dbus_uid_t _dbus_credentials_get_unix_uid (DBusCredentials *credentials); +const char* _dbus_credentials_get_windows_sid (DBusCredentials *credentials); +void * _dbus_credentials_get_adt_audit_data (DBusCredentials *credentials); +dbus_int32_t _dbus_credentials_get_adt_audit_data_size (DBusCredentials *credentials); +dbus_bool_t _dbus_credentials_are_superset (DBusCredentials *credentials, + DBusCredentials *possible_subset); +dbus_bool_t _dbus_credentials_are_empty (DBusCredentials *credentials); +dbus_bool_t _dbus_credentials_are_anonymous (DBusCredentials *credentials); +dbus_bool_t _dbus_credentials_add_credentials (DBusCredentials *credentials, + DBusCredentials *other_credentials); +/* must silently allow 'which' to not exist */ +dbus_bool_t _dbus_credentials_add_credential (DBusCredentials *credentials, + DBusCredentialType which, + DBusCredentials *other_credentials); +void _dbus_credentials_clear (DBusCredentials *credentials); +DBusCredentials* _dbus_credentials_copy (DBusCredentials *credentials); +dbus_bool_t _dbus_credentials_same_user (DBusCredentials *credentials, + DBusCredentials *other_credentials); + + +DBUS_END_DECLS + +#endif /* DBUS_CREDENTIALS_H */ diff --git a/src/dbus/dbus-dataslot.c b/src/dbus/dbus-dataslot.c new file mode 100644 index 0000000..a7ad2e4 --- /dev/null +++ b/src/dbus/dbus-dataslot.c @@ -0,0 +1,477 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-dataslot.c storing data on objects + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include "dbus-dataslot.h" +#include "dbus-threads-internal.h" + +/** + * @defgroup DBusDataSlot Data slots + * @ingroup DBusInternals + * @brief Storing data by ID + * + * Types and functions related to storing data by an + * allocated ID. This is used for dbus_connection_set_data(), + * dbus_server_set_data(), etc. + * @{ + */ + +/** + * Initializes a data slot allocator object, used to assign + * integer IDs for data slots. + * + * @param allocator the allocator to initialize + */ +dbus_bool_t +_dbus_data_slot_allocator_init (DBusDataSlotAllocator *allocator) +{ + allocator->allocated_slots = NULL; + allocator->n_allocated_slots = 0; + allocator->n_used_slots = 0; + allocator->lock_loc = NULL; + + return TRUE; +} + +/** + * Allocates an integer ID to be used for storing data + * in a #DBusDataSlotList. If the value at *slot_id_p is + * not -1, this function just increments the refcount for + * the existing slot ID. If the value is -1, a new slot ID + * is allocated and stored at *slot_id_p. + * + * @param allocator the allocator + * @param mutex_loc the location lock for this allocator + * @param slot_id_p address to fill with the slot ID + * @returns #TRUE on success + */ +dbus_bool_t +_dbus_data_slot_allocator_alloc (DBusDataSlotAllocator *allocator, + DBusMutex **mutex_loc, + dbus_int32_t *slot_id_p) +{ + dbus_int32_t slot; + + _dbus_mutex_lock (*mutex_loc); + + if (allocator->n_allocated_slots == 0) + { + _dbus_assert (allocator->lock_loc == NULL); + allocator->lock_loc = mutex_loc; + } + else if (allocator->lock_loc != mutex_loc) + { + _dbus_warn_check_failed ("D-Bus threads were initialized after first using the D-Bus library. If your application does not directly initialize threads or use D-Bus, keep in mind that some library or plugin may have used D-Bus or initialized threads behind your back. You can often fix this problem by calling dbus_init_threads() or dbus_g_threads_init() early in your main() method, before D-Bus is used.\n"); + _dbus_assert_not_reached ("exiting"); + } + + if (*slot_id_p >= 0) + { + slot = *slot_id_p; + + _dbus_assert (slot < allocator->n_allocated_slots); + _dbus_assert (allocator->allocated_slots[slot].slot_id == slot); + + allocator->allocated_slots[slot].refcount += 1; + + goto out; + } + + _dbus_assert (*slot_id_p < 0); + + if (allocator->n_used_slots < allocator->n_allocated_slots) + { + slot = 0; + while (slot < allocator->n_allocated_slots) + { + if (allocator->allocated_slots[slot].slot_id < 0) + { + allocator->allocated_slots[slot].slot_id = slot; + allocator->allocated_slots[slot].refcount = 1; + allocator->n_used_slots += 1; + break; + } + ++slot; + } + + _dbus_assert (slot < allocator->n_allocated_slots); + } + else + { + DBusAllocatedSlot *tmp; + + slot = -1; + tmp = dbus_realloc (allocator->allocated_slots, + sizeof (DBusAllocatedSlot) * (allocator->n_allocated_slots + 1)); + if (tmp == NULL) + goto out; + + allocator->allocated_slots = tmp; + slot = allocator->n_allocated_slots; + allocator->n_allocated_slots += 1; + allocator->n_used_slots += 1; + allocator->allocated_slots[slot].slot_id = slot; + allocator->allocated_slots[slot].refcount = 1; + } + + _dbus_assert (slot >= 0); + _dbus_assert (slot < allocator->n_allocated_slots); + _dbus_assert (*slot_id_p < 0); + _dbus_assert (allocator->allocated_slots[slot].slot_id == slot); + _dbus_assert (allocator->allocated_slots[slot].refcount == 1); + + *slot_id_p = slot; + + _dbus_verbose ("Allocated slot %d on allocator %p total %d slots allocated %d used\n", + slot, allocator, allocator->n_allocated_slots, allocator->n_used_slots); + + out: + _dbus_mutex_unlock (*(allocator->lock_loc)); + return slot >= 0; +} + +/** + * Deallocates an ID previously allocated with + * _dbus_data_slot_allocator_alloc(). Existing data stored on + * existing #DBusDataSlotList objects with this ID will be freed when the + * data list is finalized, but may not be retrieved (and may only be + * replaced if someone else reallocates the slot). + * The slot value is reset to -1 if this is the last unref. + * + * @param allocator the allocator + * @param slot_id_p address where we store the slot + */ +void +_dbus_data_slot_allocator_free (DBusDataSlotAllocator *allocator, + dbus_int32_t *slot_id_p) +{ + _dbus_mutex_lock (*(allocator->lock_loc)); + + _dbus_assert (*slot_id_p < allocator->n_allocated_slots); + _dbus_assert (allocator->allocated_slots[*slot_id_p].slot_id == *slot_id_p); + _dbus_assert (allocator->allocated_slots[*slot_id_p].refcount > 0); + + allocator->allocated_slots[*slot_id_p].refcount -= 1; + + if (allocator->allocated_slots[*slot_id_p].refcount > 0) + { + _dbus_mutex_unlock (*(allocator->lock_loc)); + return; + } + + /* refcount is 0, free the slot */ + _dbus_verbose ("Freeing slot %d on allocator %p total %d allocated %d used\n", + *slot_id_p, allocator, allocator->n_allocated_slots, allocator->n_used_slots); + + allocator->allocated_slots[*slot_id_p].slot_id = -1; + *slot_id_p = -1; + + allocator->n_used_slots -= 1; + + if (allocator->n_used_slots == 0) + { + DBusMutex **mutex_loc = allocator->lock_loc; + + dbus_free (allocator->allocated_slots); + allocator->allocated_slots = NULL; + allocator->n_allocated_slots = 0; + allocator->lock_loc = NULL; + + _dbus_mutex_unlock (*mutex_loc); + } + else + { + _dbus_mutex_unlock (*(allocator->lock_loc)); + } +} + +/** + * Initializes a slot list. + * @param list the list to initialize. + */ +void +_dbus_data_slot_list_init (DBusDataSlotList *list) +{ + list->slots = NULL; + list->n_slots = 0; +} + +/** + * Stores a pointer in the data slot list, along with an optional + * function to be used for freeing the data when the data is set + * again, or when the slot list is finalized. The slot number must + * have been allocated with _dbus_data_slot_allocator_alloc() for the + * same allocator passed in here. The same allocator has to be used + * with the slot list every time. + * + * @param allocator the allocator to use + * @param list the data slot list + * @param slot the slot number + * @param data the data to store + * @param free_data_func finalizer function for the data + * @param old_free_func free function for any previously-existing data + * @param old_data previously-existing data, should be freed with old_free_func + * @returns #TRUE if there was enough memory to store the data + */ +dbus_bool_t +_dbus_data_slot_list_set (DBusDataSlotAllocator *allocator, + DBusDataSlotList *list, + int slot, + void *data, + DBusFreeFunction free_data_func, + DBusFreeFunction *old_free_func, + void **old_data) +{ +#ifndef DBUS_DISABLE_ASSERT + /* We need to take the allocator lock here, because the allocator could + * be e.g. realloc()ing allocated_slots. We avoid doing this if asserts + * are disabled, since then the asserts are empty. + */ + _dbus_mutex_lock (*(allocator->lock_loc)); + _dbus_assert (slot < allocator->n_allocated_slots); + _dbus_assert (allocator->allocated_slots[slot].slot_id == slot); + _dbus_mutex_unlock (*(allocator->lock_loc)); +#endif + + if (slot >= list->n_slots) + { + DBusDataSlot *tmp; + int i; + + tmp = dbus_realloc (list->slots, + sizeof (DBusDataSlot) * (slot + 1)); + if (tmp == NULL) + return FALSE; + + list->slots = tmp; + i = list->n_slots; + list->n_slots = slot + 1; + while (i < list->n_slots) + { + list->slots[i].data = NULL; + list->slots[i].free_data_func = NULL; + ++i; + } + } + + _dbus_assert (slot < list->n_slots); + + *old_data = list->slots[slot].data; + *old_free_func = list->slots[slot].free_data_func; + + list->slots[slot].data = data; + list->slots[slot].free_data_func = free_data_func; + + return TRUE; +} + +/** + * Retrieves data previously set with _dbus_data_slot_list_set_data(). + * The slot must still be allocated (must not have been freed). + * + * @param allocator the allocator slot was allocated from + * @param list the data slot list + * @param slot the slot to get data from + * @returns the data, or #NULL if not found + */ +void* +_dbus_data_slot_list_get (DBusDataSlotAllocator *allocator, + DBusDataSlotList *list, + int slot) +{ +#ifndef DBUS_DISABLE_ASSERT + /* We need to take the allocator lock here, because the allocator could + * be e.g. realloc()ing allocated_slots. We avoid doing this if asserts + * are disabled, since then the asserts are empty. + */ + _dbus_mutex_lock (*(allocator->lock_loc)); + _dbus_assert (slot >= 0); + _dbus_assert (slot < allocator->n_allocated_slots); + _dbus_assert (allocator->allocated_slots[slot].slot_id == slot); + _dbus_mutex_unlock (*(allocator->lock_loc)); +#endif + + if (slot >= list->n_slots) + return NULL; + else + return list->slots[slot].data; +} + +/** + * Frees all data slots contained in the list, calling + * application-provided free functions if they exist. + * + * @param list the list to clear + */ +void +_dbus_data_slot_list_clear (DBusDataSlotList *list) +{ + int i; + + i = 0; + while (i < list->n_slots) + { + if (list->slots[i].free_data_func) + (* list->slots[i].free_data_func) (list->slots[i].data); + list->slots[i].data = NULL; + list->slots[i].free_data_func = NULL; + ++i; + } +} + +/** + * Frees the data slot list and all data slots contained + * in it, calling application-provided free functions + * if they exist. + * + * @param list the list to free + */ +void +_dbus_data_slot_list_free (DBusDataSlotList *list) +{ + _dbus_data_slot_list_clear (list); + + dbus_free (list->slots); + list->slots = NULL; + list->n_slots = 0; +} + +/** @} */ + +#ifdef DBUS_BUILD_TESTS +#include "dbus-test.h" +#include + +static int free_counter; + +static void +test_free_slot_data_func (void *data) +{ + int i = _DBUS_POINTER_TO_INT (data); + + _dbus_assert (free_counter == i); + ++free_counter; +} + +/** + * Test function for data slots + */ +dbus_bool_t +_dbus_data_slot_test (void) +{ + DBusDataSlotAllocator allocator; + DBusDataSlotList list; + int i; + DBusFreeFunction old_free_func; + void *old_data; + DBusMutex *mutex; + + if (!_dbus_data_slot_allocator_init (&allocator)) + _dbus_assert_not_reached ("no memory for allocator"); + + _dbus_data_slot_list_init (&list); + + _dbus_mutex_new_at_location (&mutex); + if (mutex == NULL) + _dbus_assert_not_reached ("failed to alloc mutex"); + +#define N_SLOTS 100 + + i = 0; + while (i < N_SLOTS) + { + /* we don't really want apps to rely on this ordered + * allocation, but it simplifies things to rely on it + * here. + */ + dbus_int32_t tmp = -1; + + _dbus_data_slot_allocator_alloc (&allocator, &mutex, &tmp); + + if (tmp != i) + _dbus_assert_not_reached ("did not allocate slots in numeric order\n"); + + ++i; + } + + i = 0; + while (i < N_SLOTS) + { + if (!_dbus_data_slot_list_set (&allocator, &list, + i, + _DBUS_INT_TO_POINTER (i), + test_free_slot_data_func, + &old_free_func, &old_data)) + _dbus_assert_not_reached ("no memory to set data"); + + _dbus_assert (old_free_func == NULL); + _dbus_assert (old_data == NULL); + + _dbus_assert (_dbus_data_slot_list_get (&allocator, &list, i) == + _DBUS_INT_TO_POINTER (i)); + + ++i; + } + + free_counter = 0; + i = 0; + while (i < N_SLOTS) + { + if (!_dbus_data_slot_list_set (&allocator, &list, + i, + _DBUS_INT_TO_POINTER (i), + test_free_slot_data_func, + &old_free_func, &old_data)) + _dbus_assert_not_reached ("no memory to set data"); + + _dbus_assert (old_free_func == test_free_slot_data_func); + _dbus_assert (_DBUS_POINTER_TO_INT (old_data) == i); + + (* old_free_func) (old_data); + _dbus_assert (i == (free_counter - 1)); + + _dbus_assert (_dbus_data_slot_list_get (&allocator, &list, i) == + _DBUS_INT_TO_POINTER (i)); + + ++i; + } + + free_counter = 0; + _dbus_data_slot_list_free (&list); + + _dbus_assert (N_SLOTS == free_counter); + + i = 0; + while (i < N_SLOTS) + { + dbus_int32_t tmp = i; + + _dbus_data_slot_allocator_free (&allocator, &tmp); + _dbus_assert (tmp == -1); + ++i; + } + + _dbus_mutex_free_at_location (&mutex); + + return TRUE; +} + +#endif /* DBUS_BUILD_TESTS */ diff --git a/src/dbus/dbus-dataslot.h b/src/dbus/dbus-dataslot.h new file mode 100644 index 0000000..e3c2099 --- /dev/null +++ b/src/dbus/dbus-dataslot.h @@ -0,0 +1,96 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-dataslot.h storing data on objects + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef DBUS_DATASLOT_H +#define DBUS_DATASLOT_H + +#include + +DBUS_BEGIN_DECLS + +typedef struct DBusDataSlotAllocator DBusDataSlotAllocator; +typedef struct DBusDataSlotList DBusDataSlotList; + +/** Opaque typedef for DBusDataSlot */ +typedef struct DBusDataSlot DBusDataSlot; +/** DBusDataSlot is used to store application data on the connection */ +struct DBusDataSlot +{ + void *data; /**< The application data */ + DBusFreeFunction free_data_func; /**< Free the application data */ +}; + +typedef struct DBusAllocatedSlot DBusAllocatedSlot; + +/** An allocated slot for storing data + */ +struct DBusAllocatedSlot +{ + dbus_int32_t slot_id; /**< ID of this slot */ + int refcount; /**< Number of uses of the slot */ +}; + +/** + * An allocator that tracks a set of slot IDs. + */ +struct DBusDataSlotAllocator +{ + DBusAllocatedSlot *allocated_slots; /**< Allocated slots */ + int n_allocated_slots; /**< number of slots malloc'd */ + int n_used_slots; /**< number of slots used */ + DBusMutex **lock_loc; /**< location of thread lock */ +}; + +/** + * Data structure that stores the actual user data set at a given + * slot. + */ +struct DBusDataSlotList +{ + DBusDataSlot *slots; /**< Data slots */ + int n_slots; /**< Slots we have storage for in data_slots */ +}; + +dbus_bool_t _dbus_data_slot_allocator_init (DBusDataSlotAllocator *allocator); +dbus_bool_t _dbus_data_slot_allocator_alloc (DBusDataSlotAllocator *allocator, + DBusMutex **mutex_loc, + int *slot_id_p); +void _dbus_data_slot_allocator_free (DBusDataSlotAllocator *allocator, + int *slot_id_p); +void _dbus_data_slot_list_init (DBusDataSlotList *list); +dbus_bool_t _dbus_data_slot_list_set (DBusDataSlotAllocator *allocator, + DBusDataSlotList *list, + int slot, + void *data, + DBusFreeFunction free_data_func, + DBusFreeFunction *old_free_func, + void **old_data); +void* _dbus_data_slot_list_get (DBusDataSlotAllocator *allocator, + DBusDataSlotList *list, + int slot); +void _dbus_data_slot_list_clear (DBusDataSlotList *list); +void _dbus_data_slot_list_free (DBusDataSlotList *list); + + +DBUS_END_DECLS + +#endif /* DBUS_DATASLOT_H */ diff --git a/src/dbus/dbus-errors.c b/src/dbus/dbus-errors.c new file mode 100644 index 0000000..6d14ff7 --- /dev/null +++ b/src/dbus/dbus-errors.c @@ -0,0 +1,417 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-errors.c Error reporting + * + * Copyright (C) 2002, 2004 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include "dbus-errors.h" +#include "dbus-internals.h" +#include "dbus-string.h" +#include "dbus-protocol.h" +#include +#include + +/** + * @defgroup DBusErrorInternals Error reporting internals + * @ingroup DBusInternals + * @brief Error reporting internals + * @{ + */ + +/** + * @def DBUS_ERROR_INIT + * + * Expands to a suitable initializer for a DBusError on the stack. + * Declaring a DBusError with: + * + * @code + * DBusError error = DBUS_ERROR_INIT; + * + * do_things_with (&error); + * @endcode + * + * is a more concise form of: + * + * @code + * DBusError error; + * + * dbus_error_init (&error); + * do_things_with (&error); + * @endcode + */ + +/** + * Internals of DBusError + */ +typedef struct +{ + char *name; /**< error name */ + char *message; /**< error message */ + + unsigned int const_message : 1; /**< Message is not owned by DBusError */ + + unsigned int dummy2 : 1; /**< placeholder */ + unsigned int dummy3 : 1; /**< placeholder */ + unsigned int dummy4 : 1; /**< placeholder */ + unsigned int dummy5 : 1; /**< placeholder */ + + void *padding1; /**< placeholder */ + +} DBusRealError; + +/** + * Returns a longer message describing an error name. + * If the error name is unknown, returns the name + * itself. + * + * @param error the error to describe + * @returns a constant string describing the error. + */ +static const char* +message_from_error (const char *error) +{ + if (strcmp (error, DBUS_ERROR_FAILED) == 0) + return "Unknown error"; + else if (strcmp (error, DBUS_ERROR_NO_MEMORY) == 0) + return "Not enough memory available"; + else if (strcmp (error, DBUS_ERROR_IO_ERROR) == 0) + return "Error reading or writing data"; + else if (strcmp (error, DBUS_ERROR_BAD_ADDRESS) == 0) + return "Could not parse address"; + else if (strcmp (error, DBUS_ERROR_NOT_SUPPORTED) == 0) + return "Feature not supported"; + else if (strcmp (error, DBUS_ERROR_LIMITS_EXCEEDED) == 0) + return "Resource limits exceeded"; + else if (strcmp (error, DBUS_ERROR_ACCESS_DENIED) == 0) + return "Permission denied"; + else if (strcmp (error, DBUS_ERROR_AUTH_FAILED) == 0) + return "Could not authenticate to server"; + else if (strcmp (error, DBUS_ERROR_NO_SERVER) == 0) + return "No server available at address"; + else if (strcmp (error, DBUS_ERROR_TIMEOUT) == 0) + return "Connection timed out"; + else if (strcmp (error, DBUS_ERROR_NO_NETWORK) == 0) + return "Network unavailable"; + else if (strcmp (error, DBUS_ERROR_ADDRESS_IN_USE) == 0) + return "Address already in use"; + else if (strcmp (error, DBUS_ERROR_DISCONNECTED) == 0) + return "Disconnected."; + else if (strcmp (error, DBUS_ERROR_INVALID_ARGS) == 0) + return "Invalid arguments."; + else if (strcmp (error, DBUS_ERROR_NO_REPLY) == 0) + return "Did not get a reply message."; + else if (strcmp (error, DBUS_ERROR_FILE_NOT_FOUND) == 0) + return "File doesn't exist."; + else if (strcmp (error, DBUS_ERROR_OBJECT_PATH_IN_USE) == 0) + return "Object path already in use"; + else + return error; +} + +/** @} */ /* End of internals */ + +/** + * @defgroup DBusErrors Error reporting + * @ingroup DBus + * @brief Error reporting + * + * Types and functions related to reporting errors. + * + * + * In essence D-Bus error reporting works as follows: + * + * @code + * DBusError error; + * dbus_error_init (&error); + * dbus_some_function (arg1, arg2, &error); + * if (dbus_error_is_set (&error)) + * { + * fprintf (stderr, "an error occurred: %s\n", error.message); + * dbus_error_free (&error); + * } + * @endcode + * + * By convention, all functions allow #NULL instead of a DBusError*, + * so callers who don't care about the error can ignore it. + * + * There are some rules. An error passed to a D-Bus function must + * always be unset; you can't pass in an error that's already set. If + * a function has a return code indicating whether an error occurred, + * and also a #DBusError parameter, then the error will always be set + * if and only if the return code indicates an error occurred. i.e. + * the return code and the error are never going to disagree. + * + * An error only needs to be freed if it's been set, not if + * it's merely been initialized. + * + * You can check the specific error that occurred using + * dbus_error_has_name(). + * + * Errors will not be set for programming errors, such as passing + * invalid arguments to the libdbus API. Instead, libdbus will print + * warnings, exit on a failed assertion, or even crash in those cases + * (in other words, incorrect use of the API results in undefined + * behavior, possibly accompanied by helpful debugging output if + * you're lucky). + * + * @{ + */ + +/** + * Initializes a DBusError structure. Does not allocate any memory; + * the error only needs to be freed if it is set at some point. + * + * @param error the DBusError. + */ +void +dbus_error_init (DBusError *error) +{ + DBusRealError *real; + + _dbus_return_if_fail (error != NULL); + + _dbus_assert (sizeof (DBusError) == sizeof (DBusRealError)); + + real = (DBusRealError *)error; + + real->name = NULL; + real->message = NULL; + + real->const_message = TRUE; +} + +/** + * Frees an error that's been set (or just initialized), + * then reinitializes the error as in dbus_error_init(). + * + * @param error memory where the error is stored. + */ +void +dbus_error_free (DBusError *error) +{ + DBusRealError *real; + + _dbus_return_if_fail (error != NULL); + + real = (DBusRealError *)error; + + if (!real->const_message) + { + dbus_free (real->name); + dbus_free (real->message); + } + + dbus_error_init (error); +} + +/** + * Assigns an error name and message to a DBusError. Does nothing if + * error is #NULL. The message may be #NULL, which means a default + * message will be deduced from the name. The default message will be + * totally useless, though, so using a #NULL message is not recommended. + * + * Because this function does not copy the error name or message, you + * must ensure the name and message are global data that won't be + * freed. You probably want dbus_set_error() instead, in most cases. + * + * @param error the error.or #NULL + * @param name the error name (not copied!!!) + * @param message the error message (not copied!!!) + */ +void +dbus_set_error_const (DBusError *error, + const char *name, + const char *message) +{ + DBusRealError *real; + + _dbus_return_if_error_is_set (error); + _dbus_return_if_fail (name != NULL); + + if (error == NULL) + return; + + _dbus_assert (error->name == NULL); + _dbus_assert (error->message == NULL); + + if (message == NULL) + message = message_from_error (name); + + real = (DBusRealError *)error; + + real->name = (char*) name; + real->message = (char *)message; + real->const_message = TRUE; +} + +/** + * Moves an error src into dest, freeing src and + * overwriting dest. Both src and dest must be initialized. + * src is reinitialized to an empty error. dest may not + * contain an existing error. If the destination is + * #NULL, just frees and reinits the source error. + * + * @param src the source error + * @param dest the destination error or #NULL + */ +void +dbus_move_error (DBusError *src, + DBusError *dest) +{ + _dbus_return_if_error_is_set (dest); + + if (dest) + { + dbus_error_free (dest); + *dest = *src; + dbus_error_init (src); + } + else + dbus_error_free (src); +} + +/** + * Checks whether the error is set and has the given + * name. + * @param error the error + * @param name the name + * @returns #TRUE if the given named error occurred + */ +dbus_bool_t +dbus_error_has_name (const DBusError *error, + const char *name) +{ + _dbus_return_val_if_fail (error != NULL, FALSE); + _dbus_return_val_if_fail (name != NULL, FALSE); + + _dbus_assert ((error->name != NULL && error->message != NULL) || + (error->name == NULL && error->message == NULL)); + + if (error->name != NULL) + { + DBusString str1, str2; + _dbus_string_init_const (&str1, error->name); + _dbus_string_init_const (&str2, name); + return _dbus_string_equal (&str1, &str2); + } + else + return FALSE; +} + +/** + * Checks whether an error occurred (the error is set). + * + * @param error the error object + * @returns #TRUE if an error occurred + */ +dbus_bool_t +dbus_error_is_set (const DBusError *error) +{ + _dbus_return_val_if_fail (error != NULL, FALSE); + _dbus_assert ((error->name != NULL && error->message != NULL) || + (error->name == NULL && error->message == NULL)); + return error->name != NULL; +} + +/** + * Assigns an error name and message to a DBusError. + * Does nothing if error is #NULL. + * + * The format may be #NULL, which means a (pretty much useless) + * default message will be deduced from the name. This is not a good + * idea, just go ahead and provide a useful error message. It won't + * hurt you. + * + * If no memory can be allocated for the error message, + * an out-of-memory error message will be set instead. + * + * @param error the error.or #NULL + * @param name the error name + * @param format printf-style format string. + */ +void +dbus_set_error (DBusError *error, + const char *name, + const char *format, + ...) +{ + DBusRealError *real; + DBusString str; + va_list args; + + if (error == NULL) + return; + + /* it's a bug to pile up errors */ + _dbus_return_if_error_is_set (error); + _dbus_return_if_fail (name != NULL); + + _dbus_assert (error->name == NULL); + _dbus_assert (error->message == NULL); + + if (!_dbus_string_init (&str)) + goto nomem; + + if (format == NULL) + { + if (!_dbus_string_append (&str, + message_from_error (name))) + { + _dbus_string_free (&str); + goto nomem; + } + } + else + { + va_start (args, format); + if (!_dbus_string_append_printf_valist (&str, format, args)) + { + _dbus_string_free (&str); + va_end (args); + goto nomem; + } + va_end (args); + } + + real = (DBusRealError *)error; + + if (!_dbus_string_steal_data (&str, &real->message)) + { + _dbus_string_free (&str); + goto nomem; + } + _dbus_string_free (&str); + + real->name = _dbus_strdup (name); + if (real->name == NULL) + { + dbus_free (real->message); + real->message = NULL; + goto nomem; + } + real->const_message = FALSE; + + return; + + nomem: + _DBUS_SET_OOM (error); +} + +/** @} */ /* End public API */ diff --git a/src/dbus/dbus-errors.h b/src/dbus/dbus-errors.h new file mode 100644 index 0000000..0a480d8 --- /dev/null +++ b/src/dbus/dbus-errors.h @@ -0,0 +1,82 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-errors.h Error reporting + * + * Copyright (C) 2002 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION) +#error "Only can be included directly, this file may disappear or change contents." +#endif + +#ifndef DBUS_ERROR_H +#define DBUS_ERROR_H + +#include +#include + +DBUS_BEGIN_DECLS + +/** + * @addtogroup DBusErrors + * @{ + */ + +/** Mostly-opaque type representing an error that occurred */ +typedef struct DBusError DBusError; + +/** + * Object representing an exception. + */ +struct DBusError +{ + const char *name; /**< public error name field */ + const char *message; /**< public error message field */ + + unsigned int dummy1 : 1; /**< placeholder */ + unsigned int dummy2 : 1; /**< placeholder */ + unsigned int dummy3 : 1; /**< placeholder */ + unsigned int dummy4 : 1; /**< placeholder */ + unsigned int dummy5 : 1; /**< placeholder */ + + void *padding1; /**< placeholder */ +}; + +#define DBUS_ERROR_INIT { NULL, NULL, TRUE, 0, 0, 0, 0, NULL } + +void dbus_error_init (DBusError *error); +void dbus_error_free (DBusError *error); +void dbus_set_error (DBusError *error, + const char *name, + const char *message, + ...); +void dbus_set_error_const (DBusError *error, + const char *name, + const char *message); +void dbus_move_error (DBusError *src, + DBusError *dest); +dbus_bool_t dbus_error_has_name (const DBusError *error, + const char *name); +dbus_bool_t dbus_error_is_set (const DBusError *error); + +/** @} */ + +DBUS_END_DECLS + +#endif /* DBUS_ERROR_H */ diff --git a/src/dbus/dbus-hash.c b/src/dbus/dbus-hash.c new file mode 100644 index 0000000..d0778bf --- /dev/null +++ b/src/dbus/dbus-hash.c @@ -0,0 +1,2193 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-hash.c Generic hash table utility (internal to D-Bus implementation) + * + * Copyright (C) 2002 Red Hat, Inc. + * Copyright (c) 1991-1993 The Regents of the University of California. + * Copyright (c) 1994 Sun Microsystems, Inc. + * + * Hash table implementation based on generic/tclHash.c from the Tcl + * source code. The original Tcl license applies to portions of the + * code from tclHash.c; the Tcl license follows this standad D-Bus + * license information. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +/* + * The following copyright applies to code from the Tcl distribution. + * + * Copyright (c) 1991-1993 The Regents of the University of California. + * Copyright (c) 1994 Sun Microsystems, Inc. + * + * This software is copyrighted by the Regents of the University of + * California, Sun Microsystems, Inc., Scriptics Corporation, and + * other parties. The following terms apply to all files associated + * with the software unless explicitly disclaimed in individual files. + * + * The authors hereby grant permission to use, copy, modify, + * distribute, and license this software and its documentation for any + * purpose, provided that existing copyright notices are retained in + * all copies and that this notice is included verbatim in any + * distributions. No written agreement, license, or royalty fee is + * required for any of the authorized uses. Modifications to this + * software may be copyrighted by their authors and need not follow + * the licensing terms described here, provided that the new terms are + * clearly indicated on the first page of each file where they apply. + * + * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL + * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, + * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND + * NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, + * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE + * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * GOVERNMENT USE: If you are acquiring this software on behalf of the + * U.S. government, the Government shall have only "Restricted Rights" + * in the software and related documentation as defined in the Federal + * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you + * are acquiring the software on behalf of the Department of Defense, + * the software shall be classified as "Commercial Computer Software" + * and the Government shall have only "Restricted Rights" as defined + * in Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the + * foregoing, the authors grant the U.S. Government and others acting + * in its behalf permission to use and distribute the software in + * accordance with the terms specified in this license. + */ + +#include "dbus-hash.h" +#include "dbus-internals.h" +#include "dbus-mempool.h" + +/** + * @defgroup DBusHashTable Hash table + * @ingroup DBusInternals + * @brief DBusHashTable data structure + * + * Types and functions related to DBusHashTable. + */ + +/** + * @defgroup DBusHashTableInternals Hash table implementation details + * @ingroup DBusInternals + * @brief DBusHashTable implementation details + * + * The guts of DBusHashTable. + * + * @{ + */ + +/** + * When there are this many entries per bucket, on average, rebuild + * the hash table to make it larger. + */ +#define REBUILD_MULTIPLIER 3 + +/** + * Takes a preliminary integer hash value and produces an index into a + * hash tables bucket list. The idea is to make it so that + * preliminary values that are arbitrarily similar will end up in + * different buckets. The hash function was taken from a + * random-number generator. (This is used to hash integers.) + * + * The down_shift drops off the high bits of the hash index, and + * decreases as we increase the number of hash buckets (to keep more + * range in the hash index). The mask also strips high bits and strips + * fewer high bits as the number of hash buckets increases. + * I don't understand two things: why is the initial downshift 28 + * to keep 4 bits when the initial mask is 011 to keep 2 bits, + * and why do we have both a mask and a downshift? + * + */ +#define RANDOM_INDEX(table, i) \ + (((((long) (i))*1103515245) >> (table)->down_shift) & (table)->mask) + +/** + * Initial number of buckets in hash table (hash table statically + * allocates its buckets for this size and below). + * The initial mask has to be synced to this. + */ +#define DBUS_SMALL_HASH_TABLE 4 + +/** + * Typedef for DBusHashEntry + */ +typedef struct DBusHashEntry DBusHashEntry; + +/** + * @brief Internal representation of a hash entry. + * + * A single entry (key-value pair) in the hash table. + * Internal to hash table implementation. + */ +struct DBusHashEntry +{ + DBusHashEntry *next; /**< Pointer to next entry in this + * hash bucket, or #NULL for end of + * chain. + */ + void *key; /**< Hash key */ + void *value; /**< Hash value */ +}; + +/** + * Function used to find and optionally create a hash entry. + */ +typedef DBusHashEntry* (* DBusFindEntryFunction) (DBusHashTable *table, + void *key, + dbus_bool_t create_if_not_found, + DBusHashEntry ***bucket, + DBusPreallocatedHash *preallocated); + +/** + * @brief Internals of DBusHashTable. + * + * Hash table internals. Hash tables are opaque objects, they must be + * used via accessor functions. + */ +struct DBusHashTable { + int refcount; /**< Reference count */ + + DBusHashEntry **buckets; /**< Pointer to bucket array. Each + * element points to first entry in + * bucket's hash chain, or #NULL. + */ + DBusHashEntry *static_buckets[DBUS_SMALL_HASH_TABLE]; + /**< Bucket array used for small tables + * (to avoid mallocs and frees). + */ + int n_buckets; /**< Total number of buckets allocated + * at **buckets. + */ + int n_entries; /**< Total number of entries present + * in table. + */ + int hi_rebuild_size; /**< Enlarge table when n_entries gets + * to be this large. + */ + int lo_rebuild_size; /**< Shrink table when n_entries gets + * below this. + */ + int down_shift; /**< Shift count used in hashing + * function. Designed to use high- + * order bits of randomized keys. + */ + int mask; /**< Mask value used in hashing + * function. + */ + DBusHashType key_type; /**< Type of keys used in this table */ + + + DBusFindEntryFunction find_function; /**< Function for finding entries */ + + DBusFreeFunction free_key_function; /**< Function to free keys */ + DBusFreeFunction free_value_function; /**< Function to free values */ + + DBusMemPool *entry_pool; /**< Memory pool for hash entries */ +}; + +/** + * @brief Internals of DBusHashIter. + */ +typedef struct +{ + DBusHashTable *table; /**< Pointer to table containing entry. */ + DBusHashEntry **bucket; /**< Pointer to bucket that points to + * first entry in this entry's chain: + * used for deleting the entry. + */ + DBusHashEntry *entry; /**< Current hash entry */ + DBusHashEntry *next_entry; /**< Next entry to be iterated onto in current bucket */ + int next_bucket; /**< index of next bucket */ + int n_entries_on_init; /**< used to detect table resize since initialization */ +} DBusRealHashIter; + +static DBusHashEntry* find_direct_function (DBusHashTable *table, + void *key, + dbus_bool_t create_if_not_found, + DBusHashEntry ***bucket, + DBusPreallocatedHash *preallocated); +static DBusHashEntry* find_string_function (DBusHashTable *table, + void *key, + dbus_bool_t create_if_not_found, + DBusHashEntry ***bucket, + DBusPreallocatedHash *preallocated); +#ifdef DBUS_BUILD_TESTS +static DBusHashEntry* find_two_strings_function (DBusHashTable *table, + void *key, + dbus_bool_t create_if_not_found, + DBusHashEntry ***bucket, + DBusPreallocatedHash *preallocated); +#endif +static unsigned int string_hash (const char *str); +#ifdef DBUS_BUILD_TESTS +static unsigned int two_strings_hash (const char *str); +#endif +static void rebuild_table (DBusHashTable *table); +static DBusHashEntry* alloc_entry (DBusHashTable *table); +static void remove_entry (DBusHashTable *table, + DBusHashEntry **bucket, + DBusHashEntry *entry); +static void free_entry (DBusHashTable *table, + DBusHashEntry *entry); +static void free_entry_data (DBusHashTable *table, + DBusHashEntry *entry); + + +/** @} */ + +/** + * @addtogroup DBusHashTable + * @{ + */ + +/** + * @typedef DBusHashIter + * + * Public opaque hash table iterator object. + */ + +/** + * @typedef DBusHashTable + * + * Public opaque hash table object. + */ + +/** + * @typedef DBusHashType + * + * Indicates the type of a key in the hash table. + */ + +/** + * Constructs a new hash table. Should be freed with + * _dbus_hash_table_unref(). If memory cannot be + * allocated for the hash table, returns #NULL. + * + * @param type the type of hash key to use. + * @param key_free_function function to free hash keys. + * @param value_free_function function to free hash values. + * @returns a new DBusHashTable or #NULL if no memory. + */ +DBusHashTable* +_dbus_hash_table_new (DBusHashType type, + DBusFreeFunction key_free_function, + DBusFreeFunction value_free_function) +{ + DBusHashTable *table; + DBusMemPool *entry_pool; + + table = dbus_new0 (DBusHashTable, 1); + if (table == NULL) + return NULL; + + entry_pool = _dbus_mem_pool_new (sizeof (DBusHashEntry), TRUE); + if (entry_pool == NULL) + { + dbus_free (table); + return NULL; + } + + table->refcount = 1; + table->entry_pool = entry_pool; + + _dbus_assert (DBUS_SMALL_HASH_TABLE == _DBUS_N_ELEMENTS (table->static_buckets)); + + table->buckets = table->static_buckets; + table->n_buckets = DBUS_SMALL_HASH_TABLE; + table->n_entries = 0; + table->hi_rebuild_size = DBUS_SMALL_HASH_TABLE * REBUILD_MULTIPLIER; + table->lo_rebuild_size = 0; + table->down_shift = 28; + table->mask = 3; + table->key_type = type; + + _dbus_assert (table->mask < table->n_buckets); + + switch (table->key_type) + { + case DBUS_HASH_INT: + case DBUS_HASH_POINTER: + case DBUS_HASH_ULONG: + table->find_function = find_direct_function; + break; + case DBUS_HASH_STRING: + table->find_function = find_string_function; + break; + case DBUS_HASH_TWO_STRINGS: +#ifdef DBUS_BUILD_TESTS + table->find_function = find_two_strings_function; +#endif + break; + default: + _dbus_assert_not_reached ("Unknown hash table type"); + break; + } + + table->free_key_function = key_free_function; + table->free_value_function = value_free_function; + + return table; +} + + +/** + * Increments the reference count for a hash table. + * + * @param table the hash table to add a reference to. + * @returns the hash table. + */ +DBusHashTable * +_dbus_hash_table_ref (DBusHashTable *table) +{ + table->refcount += 1; + + return table; +} + +/** + * Decrements the reference count for a hash table, + * freeing the hash table if the count reaches zero. + * + * @param table the hash table to remove a reference from. + */ +void +_dbus_hash_table_unref (DBusHashTable *table) +{ + table->refcount -= 1; + + if (table->refcount == 0) + { +#if 0 + DBusHashEntry *entry; + DBusHashEntry *next; + int i; + + /* Free the entries in the table. */ + for (i = 0; i < table->n_buckets; i++) + { + entry = table->buckets[i]; + while (entry != NULL) + { + next = entry->next; + + free_entry (table, entry); + + entry = next; + } + } +#else + DBusHashEntry *entry; + int i; + + /* Free the entries in the table. */ + for (i = 0; i < table->n_buckets; i++) + { + entry = table->buckets[i]; + while (entry != NULL) + { + free_entry_data (table, entry); + + entry = entry->next; + } + } + /* We can do this very quickly with memory pools ;-) */ + _dbus_mem_pool_free (table->entry_pool); +#endif + + /* Free the bucket array, if it was dynamically allocated. */ + if (table->buckets != table->static_buckets) + dbus_free (table->buckets); + + dbus_free (table); + } +} + +/** + * Removed all entries from a hash table. + * + * @param table the hash table to remove all entries from. + */ +void +_dbus_hash_table_remove_all (DBusHashTable *table) +{ + DBusHashIter iter; + _dbus_hash_iter_init (table, &iter); + while (_dbus_hash_iter_next (&iter)) + { + _dbus_hash_iter_remove_entry(&iter); + } +} + +static DBusHashEntry* +alloc_entry (DBusHashTable *table) +{ + DBusHashEntry *entry; + + entry = _dbus_mem_pool_alloc (table->entry_pool); + + return entry; +} + +static void +free_entry_data (DBusHashTable *table, + DBusHashEntry *entry) +{ + if (table->free_key_function) + (* table->free_key_function) (entry->key); + if (table->free_value_function) + (* table->free_value_function) (entry->value); +} + +static void +free_entry (DBusHashTable *table, + DBusHashEntry *entry) +{ + free_entry_data (table, entry); + _dbus_mem_pool_dealloc (table->entry_pool, entry); +} + +static void +remove_entry (DBusHashTable *table, + DBusHashEntry **bucket, + DBusHashEntry *entry) +{ + _dbus_assert (table != NULL); + _dbus_assert (bucket != NULL); + _dbus_assert (*bucket != NULL); + _dbus_assert (entry != NULL); + + if (*bucket == entry) + *bucket = entry->next; + else + { + DBusHashEntry *prev; + prev = *bucket; + + while (prev->next != entry) + prev = prev->next; + + _dbus_assert (prev != NULL); + + prev->next = entry->next; + } + + table->n_entries -= 1; + free_entry (table, entry); +} + +/** + * Initializes a hash table iterator. To iterate over all entries in a + * hash table, use the following code (the printf assumes a hash + * from strings to strings obviously): + * + * @code + * DBusHashIter iter; + * + * _dbus_hash_iter_init (table, &iter); + * while (_dbus_hash_iter_next (&iter)) + * { + * printf ("The first key is %s and value is %s\n", + * _dbus_hash_iter_get_string_key (&iter), + * _dbus_hash_iter_get_value (&iter)); + * } + * + * + * @endcode + * + * The iterator is initialized pointing "one before" the first hash + * entry. The first call to _dbus_hash_iter_next() moves it onto + * the first valid entry or returns #FALSE if the hash table is + * empty. Subsequent calls move to the next valid entry or return + * #FALSE if there are no more entries. + * + * Note that it is guaranteed to be safe to remove a hash entry during + * iteration, but it is not safe to add a hash entry. + * + * @param table the hash table to iterate over. + * @param iter the iterator to initialize. + */ +void +_dbus_hash_iter_init (DBusHashTable *table, + DBusHashIter *iter) +{ + DBusRealHashIter *real; + + _dbus_assert (sizeof (DBusHashIter) == sizeof (DBusRealHashIter)); + + real = (DBusRealHashIter*) iter; + + real->table = table; + real->bucket = NULL; + real->entry = NULL; + real->next_entry = NULL; + real->next_bucket = 0; + real->n_entries_on_init = table->n_entries; +} + +/** + * Move the hash iterator forward one step, to the next hash entry. + * The documentation for _dbus_hash_iter_init() explains in more + * detail. + * + * @param iter the iterator to move forward. + * @returns #FALSE if there are no more entries to move to. + */ +dbus_bool_t +_dbus_hash_iter_next (DBusHashIter *iter) +{ + DBusRealHashIter *real; + + _dbus_assert (sizeof (DBusHashIter) == sizeof (DBusRealHashIter)); + + real = (DBusRealHashIter*) iter; + + /* if this assertion failed someone probably added hash entries + * during iteration, which is bad. + */ + _dbus_assert (real->n_entries_on_init >= real->table->n_entries); + + /* Remember that real->entry may have been deleted */ + + while (real->next_entry == NULL) + { + if (real->next_bucket >= real->table->n_buckets) + { + /* invalidate iter and return false */ + real->entry = NULL; + real->table = NULL; + real->bucket = NULL; + return FALSE; + } + + real->bucket = &(real->table->buckets[real->next_bucket]); + real->next_entry = *(real->bucket); + real->next_bucket += 1; + } + + _dbus_assert (real->next_entry != NULL); + _dbus_assert (real->bucket != NULL); + + real->entry = real->next_entry; + real->next_entry = real->entry->next; + + return TRUE; +} + +/** + * Removes the current entry from the hash table. + * If a key_free_function or value_free_function + * was provided to _dbus_hash_table_new(), + * frees the key and/or value for this entry. + * + * @param iter the hash table iterator. + */ +void +_dbus_hash_iter_remove_entry (DBusHashIter *iter) +{ + DBusRealHashIter *real; + + real = (DBusRealHashIter*) iter; + + _dbus_assert (real->table != NULL); + _dbus_assert (real->entry != NULL); + _dbus_assert (real->bucket != NULL); + + remove_entry (real->table, real->bucket, real->entry); + + real->entry = NULL; /* make it crash if you try to use this entry */ +} + +/** + * Gets the value of the current entry. + * + * @param iter the hash table iterator. + */ +void* +_dbus_hash_iter_get_value (DBusHashIter *iter) +{ + DBusRealHashIter *real; + + real = (DBusRealHashIter*) iter; + + _dbus_assert (real->table != NULL); + _dbus_assert (real->entry != NULL); + + return real->entry->value; +} + +/** + * Sets the value of the current entry. + * If the hash table has a value_free_function + * it will be used to free the previous value. + * The hash table will own the passed-in value + * (it will not be copied). + * + * @param iter the hash table iterator. + * @param value the new value. + */ +void +_dbus_hash_iter_set_value (DBusHashIter *iter, + void *value) +{ + DBusRealHashIter *real; + + real = (DBusRealHashIter*) iter; + + _dbus_assert (real->table != NULL); + _dbus_assert (real->entry != NULL); + + if (real->table->free_value_function && value != real->entry->value) + (* real->table->free_value_function) (real->entry->value); + + real->entry->value = value; +} + +/** + * Gets the key for the current entry. + * Only works for hash tables of type #DBUS_HASH_INT. + * + * @param iter the hash table iterator. + */ +int +_dbus_hash_iter_get_int_key (DBusHashIter *iter) +{ + DBusRealHashIter *real; + + real = (DBusRealHashIter*) iter; + + _dbus_assert (real->table != NULL); + _dbus_assert (real->entry != NULL); + + return _DBUS_POINTER_TO_INT (real->entry->key); +} + +/** + * Gets the key for the current entry. + * Only works for hash tables of type #DBUS_HASH_ULONG. + * + * @param iter the hash table iterator. + */ +unsigned long +_dbus_hash_iter_get_ulong_key (DBusHashIter *iter) +{ + DBusRealHashIter *real; + + real = (DBusRealHashIter*) iter; + + _dbus_assert (real->table != NULL); + _dbus_assert (real->entry != NULL); + + return (unsigned long) real->entry->key; +} + +/** + * Gets the key for the current entry. + * Only works for hash tables of type #DBUS_HASH_STRING + * @param iter the hash table iterator. + */ +const char* +_dbus_hash_iter_get_string_key (DBusHashIter *iter) +{ + DBusRealHashIter *real; + + real = (DBusRealHashIter*) iter; + + _dbus_assert (real->table != NULL); + _dbus_assert (real->entry != NULL); + + return real->entry->key; +} + +#ifdef DBUS_BUILD_TESTS +/** + * Gets the key for the current entry. + * Only works for hash tables of type #DBUS_HASH_TWO_STRINGS + * @param iter the hash table iterator. + */ +const char* +_dbus_hash_iter_get_two_strings_key (DBusHashIter *iter) +{ + DBusRealHashIter *real; + + real = (DBusRealHashIter*) iter; + + _dbus_assert (real->table != NULL); + _dbus_assert (real->entry != NULL); + + return real->entry->key; +} +#endif /* DBUS_BUILD_TESTS */ + +/** + * A low-level but efficient interface for manipulating the hash + * table. It's efficient because you can get, set, and optionally + * create the hash entry while only running the hash function one + * time. + * + * Note that while calling _dbus_hash_iter_next() on the iterator + * filled in by this function may work, it's completely + * undefined which entries are after this iter and which + * are before it. So it would be silly to iterate using this + * iterator. + * + * If the hash entry is created, its value will be initialized + * to all bits zero. + * + * #FALSE may be returned due to memory allocation failure, or + * because create_if_not_found was #FALSE and the entry + * did not exist. + * + * If create_if_not_found is #TRUE and the entry is created, the hash + * table takes ownership of the key that's passed in. + * + * For a hash table of type #DBUS_HASH_INT, cast the int + * key to the key parameter using #_DBUS_INT_TO_POINTER(). + * + * @param table the hash table. + * @param key the hash key. + * @param create_if_not_found if #TRUE, create the entry if it didn't exist. + * @param iter the iterator to initialize. + * @returns #TRUE if the hash entry now exists (and the iterator is thus valid). + */ +dbus_bool_t +_dbus_hash_iter_lookup (DBusHashTable *table, + void *key, + dbus_bool_t create_if_not_found, + DBusHashIter *iter) +{ + DBusRealHashIter *real; + DBusHashEntry *entry; + DBusHashEntry **bucket; + + _dbus_assert (sizeof (DBusHashIter) == sizeof (DBusRealHashIter)); + + real = (DBusRealHashIter*) iter; + + entry = (* table->find_function) (table, key, create_if_not_found, &bucket, NULL); + + if (entry == NULL) + return FALSE; + + real->table = table; + real->bucket = bucket; + real->entry = entry; + real->next_entry = entry->next; + real->next_bucket = (bucket - table->buckets) + 1; + real->n_entries_on_init = table->n_entries; + + _dbus_assert (&(table->buckets[real->next_bucket-1]) == real->bucket); + + return TRUE; +} + +static void +add_allocated_entry (DBusHashTable *table, + DBusHashEntry *entry, + unsigned int idx, + void *key, + DBusHashEntry ***bucket) +{ + DBusHashEntry **b; + + entry->key = key; + + b = &(table->buckets[idx]); + entry->next = *b; + *b = entry; + + if (bucket) + *bucket = b; + + table->n_entries += 1; + + /* note we ONLY rebuild when ADDING - because you can iterate over a + * table and remove entries safely. + */ + if (table->n_entries >= table->hi_rebuild_size || + table->n_entries < table->lo_rebuild_size) + rebuild_table (table); +} + +static DBusHashEntry* +add_entry (DBusHashTable *table, + unsigned int idx, + void *key, + DBusHashEntry ***bucket, + DBusPreallocatedHash *preallocated) +{ + DBusHashEntry *entry; + + if (preallocated == NULL) + { + entry = alloc_entry (table); + if (entry == NULL) + { + if (bucket) + *bucket = NULL; + return NULL; + } + } + else + { + entry = (DBusHashEntry*) preallocated; + } + + add_allocated_entry (table, entry, idx, key, bucket); + + return entry; +} + +/* This is g_str_hash from GLib which was + * extensively discussed/tested/profiled + */ +static unsigned int +string_hash (const char *str) +{ + const char *p = str; + unsigned int h = *p; + + if (h) + for (p += 1; *p != '\0'; p++) + h = (h << 5) - h + *p; + + return h; +} + +#ifdef DBUS_BUILD_TESTS +/* This hashes a memory block with two nul-terminated strings + * in it, used in dbus-object-registry.c at the moment. + */ +static unsigned int +two_strings_hash (const char *str) +{ + const char *p = str; + unsigned int h = *p; + + if (h) + for (p += 1; *p != '\0'; p++) + h = (h << 5) - h + *p; + + for (p += 1; *p != '\0'; p++) + h = (h << 5) - h + *p; + + return h; +} +#endif /* DBUS_BUILD_TESTS */ + +/** Key comparison function */ +typedef int (* KeyCompareFunc) (const void *key_a, const void *key_b); + +static DBusHashEntry* +find_generic_function (DBusHashTable *table, + void *key, + unsigned int idx, + KeyCompareFunc compare_func, + dbus_bool_t create_if_not_found, + DBusHashEntry ***bucket, + DBusPreallocatedHash *preallocated) +{ + DBusHashEntry *entry; + + if (bucket) + *bucket = NULL; + + /* Search all of the entries in this bucket. */ + entry = table->buckets[idx]; + while (entry != NULL) + { + if ((compare_func == NULL && key == entry->key) || + (compare_func != NULL && (* compare_func) (key, entry->key) == 0)) + { + if (bucket) + *bucket = &(table->buckets[idx]); + + if (preallocated) + _dbus_hash_table_free_preallocated_entry (table, preallocated); + + return entry; + } + + entry = entry->next; + } + + if (create_if_not_found) + entry = add_entry (table, idx, key, bucket, preallocated); + else if (preallocated) + _dbus_hash_table_free_preallocated_entry (table, preallocated); + + return entry; +} + +static DBusHashEntry* +find_string_function (DBusHashTable *table, + void *key, + dbus_bool_t create_if_not_found, + DBusHashEntry ***bucket, + DBusPreallocatedHash *preallocated) +{ + unsigned int idx; + + idx = string_hash (key) & table->mask; + + return find_generic_function (table, key, idx, + (KeyCompareFunc) strcmp, create_if_not_found, bucket, + preallocated); +} + +#ifdef DBUS_BUILD_TESTS +static int +two_strings_cmp (const char *a, + const char *b) +{ + size_t len_a; + size_t len_b; + int res; + + res = strcmp (a, b); + if (res != 0) + return res; + + len_a = strlen (a); + len_b = strlen (b); + + return strcmp (a + len_a + 1, b + len_b + 1); +} +#endif + +#ifdef DBUS_BUILD_TESTS +static DBusHashEntry* +find_two_strings_function (DBusHashTable *table, + void *key, + dbus_bool_t create_if_not_found, + DBusHashEntry ***bucket, + DBusPreallocatedHash *preallocated) +{ + unsigned int idx; + + idx = two_strings_hash (key) & table->mask; + + return find_generic_function (table, key, idx, + (KeyCompareFunc) two_strings_cmp, create_if_not_found, bucket, + preallocated); +} +#endif /* DBUS_BUILD_TESTS */ + +static DBusHashEntry* +find_direct_function (DBusHashTable *table, + void *key, + dbus_bool_t create_if_not_found, + DBusHashEntry ***bucket, + DBusPreallocatedHash *preallocated) +{ + unsigned int idx; + + idx = RANDOM_INDEX (table, key) & table->mask; + + + return find_generic_function (table, key, idx, + NULL, create_if_not_found, bucket, + preallocated); +} + +static void +rebuild_table (DBusHashTable *table) +{ + int old_size; + int new_buckets; + DBusHashEntry **old_buckets; + DBusHashEntry **old_chain; + DBusHashEntry *entry; + dbus_bool_t growing; + + /* + * Allocate and initialize the new bucket array, and set up + * hashing constants for new array size. + */ + + growing = table->n_entries >= table->hi_rebuild_size; + + old_size = table->n_buckets; + old_buckets = table->buckets; + + if (growing) + { + /* overflow paranoia */ + if (table->n_buckets < _DBUS_INT_MAX / 4 && + table->down_shift >= 0) + new_buckets = table->n_buckets * 4; + else + return; /* can't grow anymore */ + } + else + { + new_buckets = table->n_buckets / 4; + if (new_buckets < DBUS_SMALL_HASH_TABLE) + return; /* don't bother shrinking this far */ + } + + table->buckets = dbus_new0 (DBusHashEntry*, new_buckets); + if (table->buckets == NULL) + { + /* out of memory, yay - just don't reallocate, the table will + * still work, albeit more slowly. + */ + table->buckets = old_buckets; + return; + } + + table->n_buckets = new_buckets; + + if (growing) + { + table->lo_rebuild_size = table->hi_rebuild_size; + table->hi_rebuild_size *= 4; + + table->down_shift -= 2; /* keep 2 more high bits */ + table->mask = (table->mask << 2) + 3; /* keep 2 more high bits */ + } + else + { + table->hi_rebuild_size = table->lo_rebuild_size; + table->lo_rebuild_size /= 4; + + table->down_shift += 2; /* keep 2 fewer high bits */ + table->mask = table->mask >> 2; /* keep 2 fewer high bits */ + } + +#if 0 + printf ("%s table to lo = %d hi = %d downshift = %d mask = 0x%x\n", + growing ? "GROW" : "SHRINK", + table->lo_rebuild_size, + table->hi_rebuild_size, + table->down_shift, + table->mask); +#endif + + _dbus_assert (table->lo_rebuild_size >= 0); + _dbus_assert (table->hi_rebuild_size > table->lo_rebuild_size); + _dbus_assert (table->mask != 0); + /* the mask is essentially the max index */ + _dbus_assert (table->mask < table->n_buckets); + + /* + * Rehash all of the existing entries into the new bucket array. + */ + + for (old_chain = old_buckets; old_size > 0; old_size--, old_chain++) + { + for (entry = *old_chain; entry != NULL; entry = *old_chain) + { + unsigned int idx; + DBusHashEntry **bucket; + + *old_chain = entry->next; + switch (table->key_type) + { + case DBUS_HASH_STRING: + idx = string_hash (entry->key) & table->mask; + break; + case DBUS_HASH_TWO_STRINGS: +#ifdef DBUS_BUILD_TESTS + idx = two_strings_hash (entry->key) & table->mask; +#else + idx = 0; + _dbus_assert_not_reached ("two-strings is not enabled"); +#endif + break; + case DBUS_HASH_INT: + case DBUS_HASH_ULONG: + case DBUS_HASH_POINTER: + idx = RANDOM_INDEX (table, entry->key); + break; + default: + idx = 0; + _dbus_assert_not_reached ("Unknown hash table type"); + break; + } + + bucket = &(table->buckets[idx]); + entry->next = *bucket; + *bucket = entry; + } + } + + /* Free the old bucket array, if it was dynamically allocated. */ + + if (old_buckets != table->static_buckets) + dbus_free (old_buckets); +} + +/** + * Looks up the value for a given string in a hash table + * of type #DBUS_HASH_STRING. Returns %NULL if the value + * is not present. (A not-present entry is indistinguishable + * from an entry with a value of %NULL.) + * @param table the hash table. + * @param key the string to look up. + * @returns the value of the hash entry. + */ +void* +_dbus_hash_table_lookup_string (DBusHashTable *table, + const char *key) +{ + DBusHashEntry *entry; + + _dbus_assert (table->key_type == DBUS_HASH_STRING); + + entry = (* table->find_function) (table, (char*) key, FALSE, NULL, NULL); + + if (entry) + return entry->value; + else + return NULL; +} + +#ifdef DBUS_BUILD_TESTS +/** + * Looks up the value for a given string in a hash table + * of type #DBUS_HASH_TWO_STRINGS. Returns %NULL if the value + * is not present. (A not-present entry is indistinguishable + * from an entry with a value of %NULL.) + * @param table the hash table. + * @param key the string to look up. + * @returns the value of the hash entry. + */ +void* +_dbus_hash_table_lookup_two_strings (DBusHashTable *table, + const char *key) +{ + DBusHashEntry *entry; + + _dbus_assert (table->key_type == DBUS_HASH_TWO_STRINGS); + + entry = (* table->find_function) (table, (char*) key, FALSE, NULL, NULL); + + if (entry) + return entry->value; + else + return NULL; +} +#endif /* DBUS_BUILD_TESTS */ + +/** + * Looks up the value for a given integer in a hash table + * of type #DBUS_HASH_INT. Returns %NULL if the value + * is not present. (A not-present entry is indistinguishable + * from an entry with a value of %NULL.) + * @param table the hash table. + * @param key the integer to look up. + * @returns the value of the hash entry. + */ +void* +_dbus_hash_table_lookup_int (DBusHashTable *table, + int key) +{ + DBusHashEntry *entry; + + _dbus_assert (table->key_type == DBUS_HASH_INT); + + entry = (* table->find_function) (table, _DBUS_INT_TO_POINTER (key), FALSE, NULL, NULL); + + if (entry) + return entry->value; + else + return NULL; +} + +#ifdef DBUS_BUILD_TESTS +/* disabled since it's only used for testing */ +/** + * Looks up the value for a given integer in a hash table + * of type #DBUS_HASH_POINTER. Returns %NULL if the value + * is not present. (A not-present entry is indistinguishable + * from an entry with a value of %NULL.) + * @param table the hash table. + * @param key the integer to look up. + * @returns the value of the hash entry. + */ +void* +_dbus_hash_table_lookup_pointer (DBusHashTable *table, + void *key) +{ + DBusHashEntry *entry; + + _dbus_assert (table->key_type == DBUS_HASH_POINTER); + + entry = (* table->find_function) (table, key, FALSE, NULL, NULL); + + if (entry) + return entry->value; + else + return NULL; +} +#endif /* DBUS_BUILD_TESTS */ + +/** + * Looks up the value for a given integer in a hash table + * of type #DBUS_HASH_ULONG. Returns %NULL if the value + * is not present. (A not-present entry is indistinguishable + * from an entry with a value of %NULL.) + * @param table the hash table. + * @param key the integer to look up. + * @returns the value of the hash entry. + */ +void* +_dbus_hash_table_lookup_ulong (DBusHashTable *table, + unsigned long key) +{ + DBusHashEntry *entry; + + _dbus_assert (table->key_type == DBUS_HASH_ULONG); + + entry = (* table->find_function) (table, (void*) key, FALSE, NULL, NULL); + + if (entry) + return entry->value; + else + return NULL; +} + +/** + * Removes the hash entry for the given key. If no hash entry + * for the key exists, does nothing. + * + * @param table the hash table. + * @param key the hash key. + * @returns #TRUE if the entry existed + */ +dbus_bool_t +_dbus_hash_table_remove_string (DBusHashTable *table, + const char *key) +{ + DBusHashEntry *entry; + DBusHashEntry **bucket; + + _dbus_assert (table->key_type == DBUS_HASH_STRING); + + entry = (* table->find_function) (table, (char*) key, FALSE, &bucket, NULL); + + if (entry) + { + remove_entry (table, bucket, entry); + return TRUE; + } + else + return FALSE; +} + +#ifdef DBUS_BUILD_TESTS +/** + * Removes the hash entry for the given key. If no hash entry + * for the key exists, does nothing. + * + * @param table the hash table. + * @param key the hash key. + * @returns #TRUE if the entry existed + */ +dbus_bool_t +_dbus_hash_table_remove_two_strings (DBusHashTable *table, + const char *key) +{ + DBusHashEntry *entry; + DBusHashEntry **bucket; + + _dbus_assert (table->key_type == DBUS_HASH_TWO_STRINGS); + + entry = (* table->find_function) (table, (char*) key, FALSE, &bucket, NULL); + + if (entry) + { + remove_entry (table, bucket, entry); + return TRUE; + } + else + return FALSE; +} +#endif /* DBUS_BUILD_TESTS */ + +/** + * Removes the hash entry for the given key. If no hash entry + * for the key exists, does nothing. + * + * @param table the hash table. + * @param key the hash key. + * @returns #TRUE if the entry existed + */ +dbus_bool_t +_dbus_hash_table_remove_int (DBusHashTable *table, + int key) +{ + DBusHashEntry *entry; + DBusHashEntry **bucket; + + _dbus_assert (table->key_type == DBUS_HASH_INT); + + entry = (* table->find_function) (table, _DBUS_INT_TO_POINTER (key), FALSE, &bucket, NULL); + + if (entry) + { + remove_entry (table, bucket, entry); + return TRUE; + } + else + return FALSE; +} + +#ifdef DBUS_BUILD_TESTS +/* disabled since it's only used for testing */ +/** + * Removes the hash entry for the given key. If no hash entry + * for the key exists, does nothing. + * + * @param table the hash table. + * @param key the hash key. + * @returns #TRUE if the entry existed + */ +dbus_bool_t +_dbus_hash_table_remove_pointer (DBusHashTable *table, + void *key) +{ + DBusHashEntry *entry; + DBusHashEntry **bucket; + + _dbus_assert (table->key_type == DBUS_HASH_POINTER); + + entry = (* table->find_function) (table, key, FALSE, &bucket, NULL); + + if (entry) + { + remove_entry (table, bucket, entry); + return TRUE; + } + else + return FALSE; +} +#endif /* DBUS_BUILD_TESTS */ + +/** + * Removes the hash entry for the given key. If no hash entry + * for the key exists, does nothing. + * + * @param table the hash table. + * @param key the hash key. + * @returns #TRUE if the entry existed + */ +dbus_bool_t +_dbus_hash_table_remove_ulong (DBusHashTable *table, + unsigned long key) +{ + DBusHashEntry *entry; + DBusHashEntry **bucket; + + _dbus_assert (table->key_type == DBUS_HASH_ULONG); + + entry = (* table->find_function) (table, (void*) key, FALSE, &bucket, NULL); + + if (entry) + { + remove_entry (table, bucket, entry); + return TRUE; + } + else + return FALSE; +} + +/** + * Creates a hash entry with the given key and value. + * The key and value are not copied; they are stored + * in the hash table by reference. If an entry with the + * given key already exists, the previous key and value + * are overwritten (and freed if the hash table has + * a key_free_function and/or value_free_function). + * + * Returns #FALSE if memory for the new hash entry + * can't be allocated. + * + * @param table the hash table. + * @param key the hash entry key. + * @param value the hash entry value. + */ +dbus_bool_t +_dbus_hash_table_insert_string (DBusHashTable *table, + char *key, + void *value) +{ + DBusPreallocatedHash *preallocated; + + _dbus_assert (table->key_type == DBUS_HASH_STRING); + + preallocated = _dbus_hash_table_preallocate_entry (table); + if (preallocated == NULL) + return FALSE; + + _dbus_hash_table_insert_string_preallocated (table, preallocated, + key, value); + + return TRUE; +} + +#ifdef DBUS_BUILD_TESTS +/** + * Creates a hash entry with the given key and value. + * The key and value are not copied; they are stored + * in the hash table by reference. If an entry with the + * given key already exists, the previous key and value + * are overwritten (and freed if the hash table has + * a key_free_function and/or value_free_function). + * + * Returns #FALSE if memory for the new hash entry + * can't be allocated. + * + * @param table the hash table. + * @param key the hash entry key. + * @param value the hash entry value. + */ +dbus_bool_t +_dbus_hash_table_insert_two_strings (DBusHashTable *table, + char *key, + void *value) +{ + DBusHashEntry *entry; + + _dbus_assert (table->key_type == DBUS_HASH_TWO_STRINGS); + + entry = (* table->find_function) (table, key, TRUE, NULL, NULL); + + if (entry == NULL) + return FALSE; /* no memory */ + + if (table->free_key_function && entry->key != key) + (* table->free_key_function) (entry->key); + + if (table->free_value_function && entry->value != value) + (* table->free_value_function) (entry->value); + + entry->key = key; + entry->value = value; + + return TRUE; +} +#endif /* DBUS_BUILD_TESTS */ + +/** + * Creates a hash entry with the given key and value. + * The key and value are not copied; they are stored + * in the hash table by reference. If an entry with the + * given key already exists, the previous key and value + * are overwritten (and freed if the hash table has + * a key_free_function and/or value_free_function). + * + * Returns #FALSE if memory for the new hash entry + * can't be allocated. + * + * @param table the hash table. + * @param key the hash entry key. + * @param value the hash entry value. + */ +dbus_bool_t +_dbus_hash_table_insert_int (DBusHashTable *table, + int key, + void *value) +{ + DBusHashEntry *entry; + + _dbus_assert (table->key_type == DBUS_HASH_INT); + + entry = (* table->find_function) (table, _DBUS_INT_TO_POINTER (key), TRUE, NULL, NULL); + + if (entry == NULL) + return FALSE; /* no memory */ + + if (table->free_key_function && entry->key != _DBUS_INT_TO_POINTER (key)) + (* table->free_key_function) (entry->key); + + if (table->free_value_function && entry->value != value) + (* table->free_value_function) (entry->value); + + entry->key = _DBUS_INT_TO_POINTER (key); + entry->value = value; + + return TRUE; +} + +#ifdef DBUS_BUILD_TESTS +/* disabled since it's only used for testing */ +/** + * Creates a hash entry with the given key and value. + * The key and value are not copied; they are stored + * in the hash table by reference. If an entry with the + * given key already exists, the previous key and value + * are overwritten (and freed if the hash table has + * a key_free_function and/or value_free_function). + * + * Returns #FALSE if memory for the new hash entry + * can't be allocated. + * + * @param table the hash table. + * @param key the hash entry key. + * @param value the hash entry value. + */ +dbus_bool_t +_dbus_hash_table_insert_pointer (DBusHashTable *table, + void *key, + void *value) +{ + DBusHashEntry *entry; + + _dbus_assert (table->key_type == DBUS_HASH_POINTER); + + entry = (* table->find_function) (table, key, TRUE, NULL, NULL); + + if (entry == NULL) + return FALSE; /* no memory */ + + if (table->free_key_function && entry->key != key) + (* table->free_key_function) (entry->key); + + if (table->free_value_function && entry->value != value) + (* table->free_value_function) (entry->value); + + entry->key = key; + entry->value = value; + + return TRUE; +} +#endif /* DBUS_BUILD_TESTS */ + +/** + * Creates a hash entry with the given key and value. + * The key and value are not copied; they are stored + * in the hash table by reference. If an entry with the + * given key already exists, the previous key and value + * are overwritten (and freed if the hash table has + * a key_free_function and/or value_free_function). + * + * Returns #FALSE if memory for the new hash entry + * can't be allocated. + * + * @param table the hash table. + * @param key the hash entry key. + * @param value the hash entry value. + */ +dbus_bool_t +_dbus_hash_table_insert_ulong (DBusHashTable *table, + unsigned long key, + void *value) +{ + DBusHashEntry *entry; + + _dbus_assert (table->key_type == DBUS_HASH_ULONG); + + entry = (* table->find_function) (table, (void*) key, TRUE, NULL, NULL); + + if (entry == NULL) + return FALSE; /* no memory */ + + if (table->free_key_function && entry->key != (void*) key) + (* table->free_key_function) (entry->key); + + if (table->free_value_function && entry->value != value) + (* table->free_value_function) (entry->value); + + entry->key = (void*) key; + entry->value = value; + + return TRUE; +} + +/** + * Preallocate an opaque data blob that allows us to insert into the + * hash table at a later time without allocating any memory. + * + * @param table the hash table + * @returns the preallocated data, or #NULL if no memory + */ +DBusPreallocatedHash* +_dbus_hash_table_preallocate_entry (DBusHashTable *table) +{ + DBusHashEntry *entry; + + entry = alloc_entry (table); + + return (DBusPreallocatedHash*) entry; +} + +/** + * Frees an opaque DBusPreallocatedHash that was *not* used + * in order to insert into the hash table. + * + * @param table the hash table + * @param preallocated the preallocated data + */ +void +_dbus_hash_table_free_preallocated_entry (DBusHashTable *table, + DBusPreallocatedHash *preallocated) +{ + DBusHashEntry *entry; + + _dbus_assert (preallocated != NULL); + + entry = (DBusHashEntry*) preallocated; + + /* Don't use free_entry(), since this entry has no key/data */ + _dbus_mem_pool_dealloc (table->entry_pool, entry); +} + +/** + * Inserts a string-keyed entry into the hash table, using a + * preallocated data block from + * _dbus_hash_table_preallocate_entry(). This function cannot fail due + * to lack of memory. The DBusPreallocatedHash object is consumed and + * should not be reused or freed. Otherwise this function works + * just like _dbus_hash_table_insert_string(). + * + * @param table the hash table + * @param preallocated the preallocated data + * @param key the hash key + * @param value the value + */ +void +_dbus_hash_table_insert_string_preallocated (DBusHashTable *table, + DBusPreallocatedHash *preallocated, + char *key, + void *value) +{ + DBusHashEntry *entry; + + _dbus_assert (table->key_type == DBUS_HASH_STRING); + _dbus_assert (preallocated != NULL); + + entry = (* table->find_function) (table, key, TRUE, NULL, preallocated); + + _dbus_assert (entry != NULL); + + if (table->free_key_function && entry->key != key) + (* table->free_key_function) (entry->key); + + if (table->free_value_function && entry->value != value) + (* table->free_value_function) (entry->value); + + entry->key = key; + entry->value = value; +} + +/** + * Gets the number of hash entries in a hash table. + * + * @param table the hash table. + * @returns the number of entries in the table. + */ +int +_dbus_hash_table_get_n_entries (DBusHashTable *table) +{ + return table->n_entries; +} + +/** @} */ + +#ifdef DBUS_BUILD_TESTS +#include "dbus-test.h" +#include + +/* If you're wondering why the hash table test takes + * forever to run, it's because we call this function + * in inner loops thus making things quadratic. + */ +static int +count_entries (DBusHashTable *table) +{ + DBusHashIter iter; + int count; + + count = 0; + _dbus_hash_iter_init (table, &iter); + while (_dbus_hash_iter_next (&iter)) + ++count; + + _dbus_assert (count == _dbus_hash_table_get_n_entries (table)); + + return count; +} + +/* Copy the foo\0bar\0 double string thing */ +static char* +_dbus_strdup2 (const char *str) +{ + size_t len; + char *copy; + + if (str == NULL) + return NULL; + + len = strlen (str); + len += strlen ((str + len + 1)); + + copy = dbus_malloc (len + 2); + if (copy == NULL) + return NULL; + + memcpy (copy, str, len + 2); + + return copy; +} + +/** + * @ingroup DBusHashTableInternals + * Unit test for DBusHashTable + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_hash_test (void) +{ + int i; + DBusHashTable *table1; + DBusHashTable *table2; + DBusHashTable *table3; + DBusHashTable *table4; + DBusHashIter iter; +#define N_HASH_KEYS 5000 + char **keys; + dbus_bool_t ret = FALSE; + + keys = dbus_new (char *, N_HASH_KEYS); + if (keys == NULL) + _dbus_assert_not_reached ("no memory"); + + for (i = 0; i < N_HASH_KEYS; i++) + { + keys[i] = dbus_malloc (128); + + if (keys[i] == NULL) + _dbus_assert_not_reached ("no memory"); + } + + printf ("Computing test hash keys...\n"); + i = 0; + while (i < N_HASH_KEYS) + { + int len; + + /* all the hash keys are TWO_STRINGS, but + * then we can also use those as regular strings. + */ + + len = sprintf (keys[i], "Hash key %d", i); + sprintf (keys[i] + len + 1, "Two string %d", i); + _dbus_assert (*(keys[i] + len) == '\0'); + _dbus_assert (*(keys[i] + len + 1) != '\0'); + ++i; + } + printf ("... done.\n"); + + table1 = _dbus_hash_table_new (DBUS_HASH_STRING, + dbus_free, dbus_free); + if (table1 == NULL) + goto out; + + table2 = _dbus_hash_table_new (DBUS_HASH_INT, + NULL, dbus_free); + if (table2 == NULL) + goto out; + + table3 = _dbus_hash_table_new (DBUS_HASH_ULONG, + NULL, dbus_free); + if (table3 == NULL) + goto out; + + table4 = _dbus_hash_table_new (DBUS_HASH_TWO_STRINGS, + dbus_free, dbus_free); + if (table4 == NULL) + goto out; + + + /* Insert and remove a bunch of stuff, counting the table in between + * to be sure it's not broken and that iteration works + */ + i = 0; + while (i < 3000) + { + void *value; + char *key; + + key = _dbus_strdup (keys[i]); + if (key == NULL) + goto out; + value = _dbus_strdup ("Value!"); + if (value == NULL) + goto out; + + if (!_dbus_hash_table_insert_string (table1, + key, value)) + goto out; + + value = _dbus_strdup (keys[i]); + if (value == NULL) + goto out; + + if (!_dbus_hash_table_insert_int (table2, + i, value)) + goto out; + + value = _dbus_strdup (keys[i]); + if (value == NULL) + goto out; + + if (!_dbus_hash_table_insert_ulong (table3, + i, value)) + goto out; + + key = _dbus_strdup2 (keys[i]); + if (key == NULL) + goto out; + value = _dbus_strdup ("Value!"); + if (value == NULL) + goto out; + + if (!_dbus_hash_table_insert_two_strings (table4, + key, value)) + goto out; + + _dbus_assert (count_entries (table1) == i + 1); + _dbus_assert (count_entries (table2) == i + 1); + _dbus_assert (count_entries (table3) == i + 1); + _dbus_assert (count_entries (table4) == i + 1); + + value = _dbus_hash_table_lookup_string (table1, keys[i]); + _dbus_assert (value != NULL); + _dbus_assert (strcmp (value, "Value!") == 0); + + value = _dbus_hash_table_lookup_int (table2, i); + _dbus_assert (value != NULL); + _dbus_assert (strcmp (value, keys[i]) == 0); + + value = _dbus_hash_table_lookup_ulong (table3, i); + _dbus_assert (value != NULL); + _dbus_assert (strcmp (value, keys[i]) == 0); + + value = _dbus_hash_table_lookup_two_strings (table4, keys[i]); + _dbus_assert (value != NULL); + _dbus_assert (strcmp (value, "Value!") == 0); + + ++i; + } + + --i; + while (i >= 0) + { + _dbus_hash_table_remove_string (table1, + keys[i]); + + _dbus_hash_table_remove_int (table2, i); + + _dbus_hash_table_remove_ulong (table3, i); + + _dbus_hash_table_remove_two_strings (table4, + keys[i]); + + _dbus_assert (count_entries (table1) == i); + _dbus_assert (count_entries (table2) == i); + _dbus_assert (count_entries (table3) == i); + _dbus_assert (count_entries (table4) == i); + + --i; + } + + _dbus_hash_table_ref (table1); + _dbus_hash_table_ref (table2); + _dbus_hash_table_ref (table3); + _dbus_hash_table_ref (table4); + _dbus_hash_table_unref (table1); + _dbus_hash_table_unref (table2); + _dbus_hash_table_unref (table3); + _dbus_hash_table_unref (table4); + _dbus_hash_table_unref (table1); + _dbus_hash_table_unref (table2); + _dbus_hash_table_unref (table3); + _dbus_hash_table_unref (table4); + table3 = NULL; + + /* Insert a bunch of stuff then check + * that iteration works correctly (finds the right + * values, iter_set_value works, etc.) + */ + table1 = _dbus_hash_table_new (DBUS_HASH_STRING, + dbus_free, dbus_free); + if (table1 == NULL) + goto out; + + table2 = _dbus_hash_table_new (DBUS_HASH_INT, + NULL, dbus_free); + if (table2 == NULL) + goto out; + + i = 0; + while (i < 5000) + { + char *key; + void *value; + + key = _dbus_strdup (keys[i]); + if (key == NULL) + goto out; + value = _dbus_strdup ("Value!"); + if (value == NULL) + goto out; + + if (!_dbus_hash_table_insert_string (table1, + key, value)) + goto out; + + value = _dbus_strdup (keys[i]); + if (value == NULL) + goto out; + + if (!_dbus_hash_table_insert_int (table2, + i, value)) + goto out; + + _dbus_assert (count_entries (table1) == i + 1); + _dbus_assert (count_entries (table2) == i + 1); + + ++i; + } + + _dbus_hash_iter_init (table1, &iter); + while (_dbus_hash_iter_next (&iter)) + { + const char *key; + void *value; + + key = _dbus_hash_iter_get_string_key (&iter); + value = _dbus_hash_iter_get_value (&iter); + + _dbus_assert (_dbus_hash_table_lookup_string (table1, key) == value); + + value = _dbus_strdup ("Different value!"); + if (value == NULL) + goto out; + + _dbus_hash_iter_set_value (&iter, value); + + _dbus_assert (_dbus_hash_table_lookup_string (table1, key) == value); + } + + _dbus_hash_iter_init (table1, &iter); + while (_dbus_hash_iter_next (&iter)) + { + _dbus_hash_iter_remove_entry (&iter); + _dbus_assert (count_entries (table1) == i - 1); + --i; + } + + _dbus_hash_iter_init (table2, &iter); + while (_dbus_hash_iter_next (&iter)) + { + int key; + void *value; + + key = _dbus_hash_iter_get_int_key (&iter); + value = _dbus_hash_iter_get_value (&iter); + + _dbus_assert (_dbus_hash_table_lookup_int (table2, key) == value); + + value = _dbus_strdup ("Different value!"); + if (value == NULL) + goto out; + + _dbus_hash_iter_set_value (&iter, value); + + _dbus_assert (_dbus_hash_table_lookup_int (table2, key) == value); + } + + i = count_entries (table2); + _dbus_hash_iter_init (table2, &iter); + while (_dbus_hash_iter_next (&iter)) + { + _dbus_hash_iter_remove_entry (&iter); + _dbus_assert (count_entries (table2) + 1 == i); + --i; + } + + /* add/remove interleaved, to check that we grow/shrink the table + * appropriately + */ + i = 0; + while (i < 1000) + { + char *key; + void *value; + + key = _dbus_strdup (keys[i]); + if (key == NULL) + goto out; + + value = _dbus_strdup ("Value!"); + if (value == NULL) + goto out; + + if (!_dbus_hash_table_insert_string (table1, + key, value)) + goto out; + + ++i; + } + + --i; + while (i >= 0) + { + char *key; + void *value; + + key = _dbus_strdup (keys[i]); + if (key == NULL) + goto out; + value = _dbus_strdup ("Value!"); + if (value == NULL) + goto out; + + if (!_dbus_hash_table_remove_string (table1, keys[i])) + goto out; + + if (!_dbus_hash_table_insert_string (table1, + key, value)) + goto out; + + if (!_dbus_hash_table_remove_string (table1, keys[i])) + goto out; + + _dbus_assert (_dbus_hash_table_get_n_entries (table1) == i); + + --i; + } + + /* nuke these tables */ + _dbus_hash_table_unref (table1); + _dbus_hash_table_unref (table2); + + + /* Now do a bunch of things again using _dbus_hash_iter_lookup() to + * be sure that interface works. + */ + table1 = _dbus_hash_table_new (DBUS_HASH_STRING, + dbus_free, dbus_free); + if (table1 == NULL) + goto out; + + table2 = _dbus_hash_table_new (DBUS_HASH_INT, + NULL, dbus_free); + if (table2 == NULL) + goto out; + + i = 0; + while (i < 3000) + { + void *value; + char *key; + + key = _dbus_strdup (keys[i]); + if (key == NULL) + goto out; + value = _dbus_strdup ("Value!"); + if (value == NULL) + goto out; + + if (!_dbus_hash_iter_lookup (table1, + key, TRUE, &iter)) + goto out; + _dbus_assert (_dbus_hash_iter_get_value (&iter) == NULL); + _dbus_hash_iter_set_value (&iter, value); + + value = _dbus_strdup (keys[i]); + if (value == NULL) + goto out; + + if (!_dbus_hash_iter_lookup (table2, + _DBUS_INT_TO_POINTER (i), TRUE, &iter)) + goto out; + _dbus_assert (_dbus_hash_iter_get_value (&iter) == NULL); + _dbus_hash_iter_set_value (&iter, value); + + _dbus_assert (count_entries (table1) == i + 1); + _dbus_assert (count_entries (table2) == i + 1); + + if (!_dbus_hash_iter_lookup (table1, keys[i], FALSE, &iter)) + goto out; + + value = _dbus_hash_iter_get_value (&iter); + _dbus_assert (value != NULL); + _dbus_assert (strcmp (value, "Value!") == 0); + + /* Iterate just to be sure it works, though + * it's a stupid thing to do + */ + while (_dbus_hash_iter_next (&iter)) + ; + + if (!_dbus_hash_iter_lookup (table2, _DBUS_INT_TO_POINTER (i), FALSE, &iter)) + goto out; + + value = _dbus_hash_iter_get_value (&iter); + _dbus_assert (value != NULL); + _dbus_assert (strcmp (value, keys[i]) == 0); + + /* Iterate just to be sure it works, though + * it's a stupid thing to do + */ + while (_dbus_hash_iter_next (&iter)) + ; + + ++i; + } + + --i; + while (i >= 0) + { + if (!_dbus_hash_iter_lookup (table1, keys[i], FALSE, &iter)) + _dbus_assert_not_reached ("hash entry should have existed"); + _dbus_hash_iter_remove_entry (&iter); + + if (!_dbus_hash_iter_lookup (table2, _DBUS_INT_TO_POINTER (i), FALSE, &iter)) + _dbus_assert_not_reached ("hash entry should have existed"); + _dbus_hash_iter_remove_entry (&iter); + + _dbus_assert (count_entries (table1) == i); + _dbus_assert (count_entries (table2) == i); + + --i; + } + + _dbus_hash_table_unref (table1); + _dbus_hash_table_unref (table2); + + ret = TRUE; + + out: + for (i = 0; i < N_HASH_KEYS; i++) + dbus_free (keys[i]); + + dbus_free (keys); + + return ret; +} + +#endif /* DBUS_BUILD_TESTS */ diff --git a/src/dbus/dbus-hash.h b/src/dbus/dbus-hash.h new file mode 100644 index 0000000..661e86d --- /dev/null +++ b/src/dbus/dbus-hash.h @@ -0,0 +1,142 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-hash.h Generic hash table utility (internal to D-Bus implementation) + * + * Copyright (C) 2002 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef DBUS_HASH_H +#define DBUS_HASH_H + +#include +#include + +DBUS_BEGIN_DECLS + +/** + * @addtogroup DBusHashTable + * @{ + */ + +/** Hash iterator object. The iterator is on the stack, but its real + * fields are hidden privately. + */ +struct DBusHashIter +{ + void *dummy1; /**< Do not use. */ + void *dummy2; /**< Do not use. */ + void *dummy3; /**< Do not use. */ + void *dummy4; /**< Do not use. */ + int dummy5; /**< Do not use. */ + int dummy6; /**< Do not use. */ +}; + +typedef struct DBusHashTable DBusHashTable; +typedef struct DBusHashIter DBusHashIter; + +/* Allowing an arbitrary function as with GLib + * would be nicer for a public API, but for + * an internal API this saves typing, we can add + * more whenever we feel like it. + */ +typedef enum +{ + DBUS_HASH_STRING, /**< Hash keys are strings. */ + DBUS_HASH_TWO_STRINGS, /**< Hash key is two strings in one memory block, i.e. foo\\0bar\\0 */ + DBUS_HASH_INT, /**< Hash keys are integers. */ + DBUS_HASH_POINTER, /**< Hash keys are pointers. */ + DBUS_HASH_ULONG /**< Hash keys are unsigned long. */ +} DBusHashType; + +DBusHashTable* _dbus_hash_table_new (DBusHashType type, + DBusFreeFunction key_free_function, + DBusFreeFunction value_free_function); +DBusHashTable* _dbus_hash_table_ref (DBusHashTable *table); +void _dbus_hash_table_unref (DBusHashTable *table); +void _dbus_hash_table_remove_all (DBusHashTable *table); +void _dbus_hash_iter_init (DBusHashTable *table, + DBusHashIter *iter); +dbus_bool_t _dbus_hash_iter_next (DBusHashIter *iter); +void _dbus_hash_iter_remove_entry (DBusHashIter *iter); +void* _dbus_hash_iter_get_value (DBusHashIter *iter); +void _dbus_hash_iter_set_value (DBusHashIter *iter, + void *value); +int _dbus_hash_iter_get_int_key (DBusHashIter *iter); +const char* _dbus_hash_iter_get_string_key (DBusHashIter *iter); +const char* _dbus_hash_iter_get_two_strings_key (DBusHashIter *iter); +unsigned long _dbus_hash_iter_get_ulong_key (DBusHashIter *iter); +dbus_bool_t _dbus_hash_iter_lookup (DBusHashTable *table, + void *key, + dbus_bool_t create_if_not_found, + DBusHashIter *iter); +void* _dbus_hash_table_lookup_string (DBusHashTable *table, + const char *key); +void* _dbus_hash_table_lookup_two_strings (DBusHashTable *table, + const char *key); +void* _dbus_hash_table_lookup_int (DBusHashTable *table, + int key); +void* _dbus_hash_table_lookup_pointer (DBusHashTable *table, + void *key); +void* _dbus_hash_table_lookup_ulong (DBusHashTable *table, + unsigned long key); +dbus_bool_t _dbus_hash_table_remove_string (DBusHashTable *table, + const char *key); +dbus_bool_t _dbus_hash_table_remove_two_strings (DBusHashTable *table, + const char *key); +dbus_bool_t _dbus_hash_table_remove_int (DBusHashTable *table, + int key); +dbus_bool_t _dbus_hash_table_remove_pointer (DBusHashTable *table, + void *key); +dbus_bool_t _dbus_hash_table_remove_ulong (DBusHashTable *table, + unsigned long key); +dbus_bool_t _dbus_hash_table_insert_string (DBusHashTable *table, + char *key, + void *value); +dbus_bool_t _dbus_hash_table_insert_two_strings (DBusHashTable *table, + char *key, + void *value); +dbus_bool_t _dbus_hash_table_insert_int (DBusHashTable *table, + int key, + void *value); +dbus_bool_t _dbus_hash_table_insert_pointer (DBusHashTable *table, + void *key, + void *value); +dbus_bool_t _dbus_hash_table_insert_ulong (DBusHashTable *table, + unsigned long key, + void *value); +int _dbus_hash_table_get_n_entries (DBusHashTable *table); + +/* Preallocation */ + +/** A preallocated hash entry */ +typedef struct DBusPreallocatedHash DBusPreallocatedHash; + +DBusPreallocatedHash *_dbus_hash_table_preallocate_entry (DBusHashTable *table); +void _dbus_hash_table_free_preallocated_entry (DBusHashTable *table, + DBusPreallocatedHash *preallocated); +void _dbus_hash_table_insert_string_preallocated (DBusHashTable *table, + DBusPreallocatedHash *preallocated, + char *key, + void *value); + +/** @} */ + +DBUS_END_DECLS + +#endif /* DBUS_HASH_H */ diff --git a/src/dbus/dbus-internals.c b/src/dbus/dbus-internals.c new file mode 100644 index 0000000..f3ca7c5 --- /dev/null +++ b/src/dbus/dbus-internals.c @@ -0,0 +1,951 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-internals.c random utility stuff (internal to D-Bus implementation) + * + * Copyright (C) 2002, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include "dbus-internals.h" +#include "dbus-protocol.h" +#include "dbus-marshal-basic.h" +#include "dbus-test.h" +#include +#include +#include +#include + +/** + * @defgroup DBusInternals D-Bus secret internal implementation details + * @brief Documentation useful when developing or debugging D-Bus itself. + * + */ + +/** + * @defgroup DBusInternalsUtils Utilities and portability + * @ingroup DBusInternals + * @brief Utility functions (_dbus_assert(), _dbus_warn(), etc.) + * @{ + */ + +/** + * @def _dbus_assert + * + * Aborts with an error message if the condition is false. + * + * @param condition condition which must be true. + */ + +/** + * @def _dbus_assert_not_reached + * + * Aborts with an error message if called. + * The given explanation will be printed. + * + * @param explanation explanation of what happened if the code was reached. + */ + +/** + * @def _DBUS_N_ELEMENTS + * + * Computes the number of elements in a fixed-size array using + * sizeof(). + * + * @param array the array to count elements in. + */ + +/** + * @def _DBUS_POINTER_TO_INT + * + * Safely casts a void* to an integer; should only be used on void* + * that actually contain integers, for example one created with + * _DBUS_INT_TO_POINTER. Only guaranteed to preserve 32 bits. + * (i.e. it's used to store 32-bit ints in pointers, but + * can't be used to store 64-bit pointers in ints.) + * + * @param pointer pointer to extract an integer from. + */ +/** + * @def _DBUS_INT_TO_POINTER + * + * Safely stuffs an integer into a pointer, to be extracted later with + * _DBUS_POINTER_TO_INT. Only guaranteed to preserve 32 bits. + * + * @param integer the integer to stuff into a pointer. + */ +/** + * @def _DBUS_ZERO + * + * Sets all bits in an object to zero. + * + * @param object the object to be zeroed. + */ +/** + * @def _DBUS_INT16_MIN + * + * Minimum value of type "int16" + */ +/** + * @def _DBUS_INT16_MAX + * + * Maximum value of type "int16" + */ +/** + * @def _DBUS_UINT16_MAX + * + * Maximum value of type "uint16" + */ + +/** + * @def _DBUS_INT32_MIN + * + * Minimum value of type "int32" + */ +/** + * @def _DBUS_INT32_MAX + * + * Maximum value of type "int32" + */ +/** + * @def _DBUS_UINT32_MAX + * + * Maximum value of type "uint32" + */ + +/** + * @def _DBUS_INT_MIN + * + * Minimum value of type "int" + */ +/** + * @def _DBUS_INT_MAX + * + * Maximum value of type "int" + */ +/** + * @def _DBUS_UINT_MAX + * + * Maximum value of type "uint" + */ + +/** + * @typedef DBusForeachFunction + * + * Used to iterate over each item in a collection, such as + * a DBusList. + */ + +/** + * @def _DBUS_LOCK_NAME + * + * Expands to name of a global lock variable. + */ + +/** + * @def _DBUS_DEFINE_GLOBAL_LOCK + * + * Defines a global lock variable with the given name. + * The lock must be added to the list to initialize + * in dbus_threads_init(). + */ + +/** + * @def _DBUS_DECLARE_GLOBAL_LOCK + * + * Expands to declaration of a global lock defined + * with _DBUS_DEFINE_GLOBAL_LOCK. + * The lock must be added to the list to initialize + * in dbus_threads_init(). + */ + +/** + * @def _DBUS_LOCK + * + * Locks a global lock + */ + +/** + * @def _DBUS_UNLOCK + * + * Unlocks a global lock + */ + +/** + * Fixed "out of memory" error message, just to avoid + * making up a different string every time and wasting + * space. + */ +const char _dbus_no_memory_message[] = "Not enough memory"; + +static dbus_bool_t warn_initted = FALSE; +static dbus_bool_t fatal_warnings = FALSE; +static dbus_bool_t fatal_warnings_on_check_failed = TRUE; + +static void +init_warnings(void) +{ + if (!warn_initted) + { + const char *s; + s = _dbus_getenv ("DBUS_FATAL_WARNINGS"); + if (s && *s) + { + if (*s == '0') + { + fatal_warnings = FALSE; + fatal_warnings_on_check_failed = FALSE; + } + else if (*s == '1') + { + fatal_warnings = TRUE; + fatal_warnings_on_check_failed = TRUE; + } + else + { + fprintf(stderr, "DBUS_FATAL_WARNINGS should be set to 0 or 1 if set, not '%s'", + s); + } + } + + warn_initted = TRUE; + } +} + +/** + * Prints a warning message to stderr. Can optionally be made to exit + * fatally by setting DBUS_FATAL_WARNINGS, but this is rarely + * used. This function should be considered pretty much equivalent to + * fprintf(stderr). _dbus_warn_check_failed() on the other hand is + * suitable for use when a programming mistake has been made. + * + * @param format printf-style format string. + */ +void +_dbus_warn (const char *format, + ...) +{ + va_list args; + + if (!warn_initted) + init_warnings (); + + va_start (args, format); + vfprintf (stderr, format, args); + va_end (args); + + if (fatal_warnings) + { + fflush (stderr); + _dbus_abort (); + } +} + +/** + * Prints a "critical" warning to stderr when an assertion fails; + * differs from _dbus_warn primarily in that it prefixes the pid and + * defaults to fatal. This should be used only when a programming + * error has been detected. (NOT for unavoidable errors that an app + * might handle - those should be returned as DBusError.) Calling this + * means "there is a bug" + */ +void +_dbus_warn_check_failed(const char *format, + ...) +{ + va_list args; + + if (!warn_initted) + init_warnings (); + + fprintf (stderr, "process %lu: ", _dbus_pid_for_log ()); + + va_start (args, format); + vfprintf (stderr, format, args); + va_end (args); + + if (fatal_warnings_on_check_failed) + { + fflush (stderr); + _dbus_abort (); + } +} + +#ifdef DBUS_ENABLE_VERBOSE_MODE + +static dbus_bool_t verbose_initted = FALSE; +static dbus_bool_t verbose = TRUE; + +/** Whether to show the current thread in verbose messages */ +#define PTHREAD_IN_VERBOSE 0 +#if PTHREAD_IN_VERBOSE +#include +#endif + +#ifdef DBUS_WIN +#define inline +#endif + +static inline void +_dbus_verbose_init (void) +{ + if (!verbose_initted) + { + const char *p = _dbus_getenv ("DBUS_VERBOSE"); + verbose = p != NULL && *p == '1'; + verbose_initted = TRUE; + } +} + +/** + * Implementation of dbus_is_verbose() macro if built with verbose logging + * enabled. + * @returns whether verbose logging is active. + */ +dbus_bool_t +_dbus_is_verbose_real (void) +{ + _dbus_verbose_init (); + return verbose; +} + +/** + * Prints a warning message to stderr + * if the user has enabled verbose mode. + * This is the real function implementation, + * use _dbus_verbose() macro in code. + * + * @param format printf-style format string. + */ +void +_dbus_verbose_real (const char *format, + ...) +{ + va_list args; + static dbus_bool_t need_pid = TRUE; + int len; + + /* things are written a bit oddly here so that + * in the non-verbose case we just have the one + * conditional and return immediately. + */ + if (!_dbus_is_verbose_real()) + return; + + /* Print out pid before the line */ + if (need_pid) + { +#if PTHREAD_IN_VERBOSE + fprintf (stderr, "%lu: 0x%lx: ", _dbus_pid_for_log (), pthread_self ()); +#else + fprintf (stderr, "%lu: ", _dbus_pid_for_log ()); +#endif + } + + + /* Only print pid again if the next line is a new line */ + len = strlen (format); + if (format[len-1] == '\n') + need_pid = TRUE; + else + need_pid = FALSE; + + va_start (args, format); + vfprintf (stderr, format, args); + va_end (args); + + fflush (stderr); +} + +/** + * Reinitializes the verbose logging code, used + * as a hack in dbus-spawn.c so that a child + * process re-reads its pid + * + */ +void +_dbus_verbose_reset_real (void) +{ + verbose_initted = FALSE; +} + +#endif /* DBUS_ENABLE_VERBOSE_MODE */ + +/** + * Duplicates a string. Result must be freed with + * dbus_free(). Returns #NULL if memory allocation fails. + * If the string to be duplicated is #NULL, returns #NULL. + * + * @param str string to duplicate. + * @returns newly-allocated copy. + */ +char* +_dbus_strdup (const char *str) +{ + size_t len; + char *copy; + + if (str == NULL) + return NULL; + + len = strlen (str); + + copy = dbus_malloc (len + 1); + if (copy == NULL) + return NULL; + + memcpy (copy, str, len + 1); + + return copy; +} + +/** + * Duplicates a block of memory. Returns + * #NULL on failure. + * + * @param mem memory to copy + * @param n_bytes number of bytes to copy + * @returns the copy + */ +void* +_dbus_memdup (const void *mem, + size_t n_bytes) +{ + void *copy; + + copy = dbus_malloc (n_bytes); + if (copy == NULL) + return NULL; + + memcpy (copy, mem, n_bytes); + + return copy; +} + +/** + * Duplicates a string array. Result may be freed with + * dbus_free_string_array(). Returns #NULL if memory allocation fails. + * If the array to be duplicated is #NULL, returns #NULL. + * + * @param array array to duplicate. + * @returns newly-allocated copy. + */ +char** +_dbus_dup_string_array (const char **array) +{ + int len; + int i; + char **copy; + + if (array == NULL) + return NULL; + + for (len = 0; array[len] != NULL; ++len) + ; + + copy = dbus_new0 (char*, len + 1); + if (copy == NULL) + return NULL; + + i = 0; + while (i < len) + { + copy[i] = _dbus_strdup (array[i]); + if (copy[i] == NULL) + { + dbus_free_string_array (copy); + return NULL; + } + + ++i; + } + + return copy; +} + +/** + * Checks whether a string array contains the given string. + * + * @param array array to search. + * @param str string to look for + * @returns #TRUE if array contains string + */ +dbus_bool_t +_dbus_string_array_contains (const char **array, + const char *str) +{ + int i; + + i = 0; + while (array[i] != NULL) + { + if (strcmp (array[i], str) == 0) + return TRUE; + ++i; + } + + return FALSE; +} + +/** + * Generates a new UUID. If you change how this is done, + * there's some text about it in the spec that should also change. + * + * @param uuid the uuid to initialize + */ +void +_dbus_generate_uuid (DBusGUID *uuid) +{ + long now; + + _dbus_get_current_time (&now, NULL); + + uuid->as_uint32s[DBUS_UUID_LENGTH_WORDS - 1] = DBUS_UINT32_TO_BE (now); + + _dbus_generate_random_bytes_buffer (uuid->as_bytes, DBUS_UUID_LENGTH_BYTES - 4); +} + +/** + * Hex-encode a UUID. + * + * @param uuid the uuid + * @param encoded string to append hex uuid to + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_uuid_encode (const DBusGUID *uuid, + DBusString *encoded) +{ + DBusString binary; + _dbus_string_init_const_len (&binary, uuid->as_bytes, DBUS_UUID_LENGTH_BYTES); + return _dbus_string_hex_encode (&binary, 0, encoded, _dbus_string_get_length (encoded)); +} + +static dbus_bool_t +_dbus_read_uuid_file_without_creating (const DBusString *filename, + DBusGUID *uuid, + DBusError *error) +{ + DBusString contents; + DBusString decoded; + int end; + + if (!_dbus_string_init (&contents)) + { + _DBUS_SET_OOM (error); + return FALSE; + } + + if (!_dbus_string_init (&decoded)) + { + _dbus_string_free (&contents); + _DBUS_SET_OOM (error); + return FALSE; + } + + if (!_dbus_file_get_contents (&contents, filename, error)) + goto error; + + _dbus_string_chop_white (&contents); + + if (_dbus_string_get_length (&contents) != DBUS_UUID_LENGTH_HEX) + { + dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT, + "UUID file '%s' should contain a hex string of length %d, not length %d, with no other text", + _dbus_string_get_const_data (filename), + DBUS_UUID_LENGTH_HEX, + _dbus_string_get_length (&contents)); + goto error; + } + + if (!_dbus_string_hex_decode (&contents, 0, &end, &decoded, 0)) + { + _DBUS_SET_OOM (error); + goto error; + } + + if (end == 0) + { + dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT, + "UUID file '%s' contains invalid hex data", + _dbus_string_get_const_data (filename)); + goto error; + } + + if (_dbus_string_get_length (&decoded) != DBUS_UUID_LENGTH_BYTES) + { + dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT, + "UUID file '%s' contains %d bytes of hex-encoded data instead of %d", + _dbus_string_get_const_data (filename), + _dbus_string_get_length (&decoded), + DBUS_UUID_LENGTH_BYTES); + goto error; + } + + _dbus_string_copy_to_buffer (&decoded, uuid->as_bytes, DBUS_UUID_LENGTH_BYTES); + + _dbus_string_free (&decoded); + _dbus_string_free (&contents); + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + return TRUE; + + error: + _DBUS_ASSERT_ERROR_IS_SET (error); + _dbus_string_free (&contents); + _dbus_string_free (&decoded); + return FALSE; +} + +static dbus_bool_t +_dbus_create_uuid_file_exclusively (const DBusString *filename, + DBusGUID *uuid, + DBusError *error) +{ + DBusString encoded; + + if (!_dbus_string_init (&encoded)) + { + _DBUS_SET_OOM (error); + return FALSE; + } + + _dbus_generate_uuid (uuid); + + if (!_dbus_uuid_encode (uuid, &encoded)) + { + _DBUS_SET_OOM (error); + goto error; + } + + /* FIXME this is racy; we need a save_file_exclusively + * function. But in practice this should be fine for now. + * + * - first be sure we can create the file and it + * doesn't exist by creating it empty with O_EXCL + * - then create it by creating a temporary file and + * overwriting atomically with rename() + */ + if (!_dbus_create_file_exclusively (filename, error)) + goto error; + + if (!_dbus_string_append_byte (&encoded, '\n')) + { + _DBUS_SET_OOM (error); + goto error; + } + + if (!_dbus_string_save_to_file (&encoded, filename, error)) + goto error; + + if (!_dbus_make_file_world_readable (filename, error)) + goto error; + + _dbus_string_free (&encoded); + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + return TRUE; + + error: + _DBUS_ASSERT_ERROR_IS_SET (error); + _dbus_string_free (&encoded); + return FALSE; +} + +/** + * Reads (and optionally writes) a uuid to a file. Initializes the uuid + * unless an error is returned. + * + * @param filename the name of the file + * @param uuid uuid to be initialized with the loaded uuid + * @param create_if_not_found #TRUE to create a new uuid and save it if the file doesn't exist + * @param error the error return + * @returns #FALSE if the error is set + */ +dbus_bool_t +_dbus_read_uuid_file (const DBusString *filename, + DBusGUID *uuid, + dbus_bool_t create_if_not_found, + DBusError *error) +{ + DBusError read_error = DBUS_ERROR_INIT; + + if (_dbus_read_uuid_file_without_creating (filename, uuid, &read_error)) + return TRUE; + + if (!create_if_not_found) + { + dbus_move_error (&read_error, error); + return FALSE; + } + + /* If the file exists and contains junk, we want to keep that error + * message instead of overwriting it with a "file exists" error + * message when we try to write + */ + if (dbus_error_has_name (&read_error, DBUS_ERROR_INVALID_FILE_CONTENT)) + { + dbus_move_error (&read_error, error); + return FALSE; + } + else + { + dbus_error_free (&read_error); + return _dbus_create_uuid_file_exclusively (filename, uuid, error); + } +} + +_DBUS_DEFINE_GLOBAL_LOCK (machine_uuid); +static int machine_uuid_initialized_generation = 0; +static DBusGUID machine_uuid; + +/** + * Gets the hex-encoded UUID of the machine this function is + * executed on. This UUID is guaranteed to be the same for a given + * machine at least until it next reboots, though it also + * makes some effort to be the same forever, it may change if the + * machine is reconfigured or its hardware is modified. + * + * @param uuid_str string to append hex-encoded machine uuid to + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_get_local_machine_uuid_encoded (DBusString *uuid_str) +{ + dbus_bool_t ok; + + _DBUS_LOCK (machine_uuid); + if (machine_uuid_initialized_generation != _dbus_current_generation) + { + DBusError error = DBUS_ERROR_INIT; + + if (!_dbus_read_local_machine_uuid (&machine_uuid, FALSE, + &error)) + { +#ifndef DBUS_BUILD_TESTS + /* For the test suite, we may not be installed so just continue silently + * here. But in a production build, we want to be nice and loud about + * this. + */ + _dbus_warn_check_failed ("D-Bus library appears to be incorrectly set up; failed to read machine uuid: %s\n" + "See the manual page for dbus-uuidgen to correct this issue.\n", + error.message); +#endif + + dbus_error_free (&error); + + _dbus_generate_uuid (&machine_uuid); + } + } + + ok = _dbus_uuid_encode (&machine_uuid, uuid_str); + + _DBUS_UNLOCK (machine_uuid); + + return ok; +} + +#ifdef DBUS_BUILD_TESTS +/** + * Returns a string describing the given name. + * + * @param header_field the field to describe + * @returns a constant string describing the field + */ +const char * +_dbus_header_field_to_string (int header_field) +{ + switch (header_field) + { + case DBUS_HEADER_FIELD_INVALID: + return "invalid"; + case DBUS_HEADER_FIELD_PATH: + return "path"; + case DBUS_HEADER_FIELD_INTERFACE: + return "interface"; + case DBUS_HEADER_FIELD_MEMBER: + return "member"; + case DBUS_HEADER_FIELD_ERROR_NAME: + return "error-name"; + case DBUS_HEADER_FIELD_REPLY_SERIAL: + return "reply-serial"; + case DBUS_HEADER_FIELD_DESTINATION: + return "destination"; + case DBUS_HEADER_FIELD_SENDER: + return "sender"; + case DBUS_HEADER_FIELD_SIGNATURE: + return "signature"; + default: + return "unknown"; + } +} +#endif /* DBUS_BUILD_TESTS */ + +#ifndef DBUS_DISABLE_CHECKS +/** String used in _dbus_return_if_fail macro */ +const char _dbus_return_if_fail_warning_format[] = +"arguments to %s() were incorrect, assertion \"%s\" failed in file %s line %d.\n" +"This is normally a bug in some application using the D-Bus library.\n"; +#endif + +#ifndef DBUS_DISABLE_ASSERT +/** + * Internals of _dbus_assert(); it's a function + * rather than a macro with the inline code so + * that the assertion failure blocks don't show up + * in test suite coverage, and to shrink code size. + * + * @param condition TRUE if assertion succeeded + * @param condition_text condition as a string + * @param file file the assertion is in + * @param line line the assertion is in + * @param func function the assertion is in + */ +void +_dbus_real_assert (dbus_bool_t condition, + const char *condition_text, + const char *file, + int line, + const char *func) +{ + if (_DBUS_UNLIKELY (!condition)) + { + _dbus_warn ("%lu: assertion failed \"%s\" file \"%s\" line %d function %s\n", + _dbus_pid_for_log (), condition_text, file, line, func); + _dbus_abort (); + } +} + +/** + * Internals of _dbus_assert_not_reached(); it's a function + * rather than a macro with the inline code so + * that the assertion failure blocks don't show up + * in test suite coverage, and to shrink code size. + * + * @param explanation what was reached that shouldn't have been + * @param file file the assertion is in + * @param line line the assertion is in + */ +void +_dbus_real_assert_not_reached (const char *explanation, + const char *file, + int line) +{ + _dbus_warn ("File \"%s\" line %d process %lu should not have been reached: %s\n", + file, line, _dbus_pid_for_log (), explanation); + _dbus_abort (); +} +#endif /* DBUS_DISABLE_ASSERT */ + +#ifdef DBUS_BUILD_TESTS +static dbus_bool_t +run_failing_each_malloc (int n_mallocs, + const char *description, + DBusTestMemoryFunction func, + void *data) +{ + n_mallocs += 10; /* fudge factor to ensure reallocs etc. are covered */ + + while (n_mallocs >= 0) + { + _dbus_set_fail_alloc_counter (n_mallocs); + + _dbus_verbose ("\n===\n%s: (will fail malloc %d with %d failures)\n===\n", + description, n_mallocs, + _dbus_get_fail_alloc_failures ()); + + if (!(* func) (data)) + return FALSE; + + n_mallocs -= 1; + } + + _dbus_set_fail_alloc_counter (_DBUS_INT_MAX); + + return TRUE; +} + +/** + * Tests how well the given function responds to out-of-memory + * situations. Calls the function repeatedly, failing a different + * call to malloc() each time. If the function ever returns #FALSE, + * the test fails. The function should return #TRUE whenever something + * valid (such as returning an error, or succeeding) occurs, and #FALSE + * if it gets confused in some way. + * + * @param description description of the test used in verbose output + * @param func function to call + * @param data data to pass to function + * @returns #TRUE if the function never returns FALSE + */ +dbus_bool_t +_dbus_test_oom_handling (const char *description, + DBusTestMemoryFunction func, + void *data) +{ + int approx_mallocs; + const char *setting; + int max_failures_to_try; + int i; + + /* Run once to see about how many mallocs are involved */ + + _dbus_set_fail_alloc_counter (_DBUS_INT_MAX); + + _dbus_verbose ("Running once to count mallocs\n"); + + if (!(* func) (data)) + return FALSE; + + approx_mallocs = _DBUS_INT_MAX - _dbus_get_fail_alloc_counter (); + + _dbus_verbose ("\n=================\n%s: about %d mallocs total\n=================\n", + description, approx_mallocs); + + setting = _dbus_getenv ("DBUS_TEST_MALLOC_FAILURES"); + if (setting != NULL) + { + DBusString str; + long v; + _dbus_string_init_const (&str, setting); + v = 4; + if (!_dbus_string_parse_int (&str, 0, &v, NULL)) + _dbus_warn ("couldn't parse '%s' as integer\n", setting); + max_failures_to_try = v; + } + else + { + max_failures_to_try = 4; + } + + i = setting ? max_failures_to_try - 1 : 1; + while (i < max_failures_to_try) + { + _dbus_set_fail_alloc_failures (i); + if (!run_failing_each_malloc (approx_mallocs, description, func, data)) + return FALSE; + ++i; + } + + _dbus_verbose ("\n=================\n%s: all iterations passed\n=================\n", + description); + + return TRUE; +} +#endif /* DBUS_BUILD_TESTS */ + +/** @} */ diff --git a/src/dbus/dbus-internals.h b/src/dbus/dbus-internals.h new file mode 100644 index 0000000..3e5f989 --- /dev/null +++ b/src/dbus/dbus-internals.h @@ -0,0 +1,346 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-internals.h random utility stuff (internal to D-Bus implementation) + * + * Copyright (C) 2002, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifdef DBUS_INSIDE_DBUS_H +#error "You can't include dbus-internals.h in the public header dbus.h" +#endif + +#ifndef DBUS_INTERNALS_H +#define DBUS_INTERNALS_H + +#include + +#include +#include +#include +#include +#include + +DBUS_BEGIN_DECLS + +#define DBUS_SESSION_BUS_DEFAULT_ADDRESS "autolaunch:" + +void _dbus_warn (const char *format, + ...) _DBUS_GNUC_PRINTF (1, 2); + +void _dbus_warn_check_failed (const char *format, + ...) _DBUS_GNUC_PRINTF (1, 2); + + +#if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) +#define _DBUS_FUNCTION_NAME __func__ +#elif defined(__GNUC__) || defined(_MSC_VER) +#define _DBUS_FUNCTION_NAME __FUNCTION__ +#else +#define _DBUS_FUNCTION_NAME "unknown function" +#endif + +/* + * (code from GLib) + * + * The _DBUS_LIKELY and _DBUS_UNLIKELY macros let the programmer give hints to + * the compiler about the expected result of an expression. Some compilers + * can use this information for optimizations. + * + * The _DBUS_BOOLEAN_EXPR macro is intended to trigger a gcc warning when + * putting assignments in the macro arg + */ +#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__) +#define _DBUS_BOOLEAN_EXPR(expr) \ + __extension__ ({ \ + int _dbus_boolean_var_; \ + if (expr) \ + _dbus_boolean_var_ = 1; \ + else \ + _dbus_boolean_var_ = 0; \ + _dbus_boolean_var_; \ +}) +#define _DBUS_LIKELY(expr) (__builtin_expect (_DBUS_BOOLEAN_EXPR(expr), 1)) +#define _DBUS_UNLIKELY(expr) (__builtin_expect (_DBUS_BOOLEAN_EXPR(expr), 0)) +#else +#define _DBUS_LIKELY(expr) (expr) +#define _DBUS_UNLIKELY(expr) (expr) +#endif + +#ifdef DBUS_ENABLE_VERBOSE_MODE + +void _dbus_verbose_real (const char *format, + ...) _DBUS_GNUC_PRINTF (1, 2); +void _dbus_verbose_reset_real (void); +dbus_bool_t _dbus_is_verbose_real (void); + +# define _dbus_verbose _dbus_verbose_real +# define _dbus_verbose_reset _dbus_verbose_reset_real +# define _dbus_is_verbose _dbus_is_verbose_real +#else +# ifdef HAVE_ISO_VARARGS +# define _dbus_verbose(...) +# elif defined (HAVE_GNUC_VARARGS) +# define _dbus_verbose(format...) +# else +static void _dbus_verbose(const char * x,...) {;} +# endif +# define _dbus_verbose_reset() +# define _dbus_is_verbose() FALSE +#endif /* !DBUS_ENABLE_VERBOSE_MODE */ + +const char* _dbus_strerror (int error_number); + +#ifdef DBUS_DISABLE_ASSERT +#define _dbus_assert(condition) +#else +void _dbus_real_assert (dbus_bool_t condition, + const char *condition_text, + const char *file, + int line, + const char *func); +#define _dbus_assert(condition) \ + _dbus_real_assert ((condition) != 0, #condition, __FILE__, __LINE__, _DBUS_FUNCTION_NAME) +#endif /* !DBUS_DISABLE_ASSERT */ + +#ifdef DBUS_DISABLE_ASSERT +#define _dbus_assert_not_reached(explanation) +#else +void _dbus_real_assert_not_reached (const char *explanation, + const char *file, + int line) _DBUS_GNUC_NORETURN; +#define _dbus_assert_not_reached(explanation) \ + _dbus_real_assert_not_reached (explanation, __FILE__, __LINE__) +#endif /* !DBUS_DISABLE_ASSERT */ + +#ifdef DBUS_DISABLE_CHECKS +#define _dbus_return_if_fail(condition) +#define _dbus_return_val_if_fail(condition, val) +#else +extern const char _dbus_return_if_fail_warning_format[]; + +#define _dbus_return_if_fail(condition) do { \ + _dbus_assert ((*(const char*)_DBUS_FUNCTION_NAME) != '_'); \ + if (!(condition)) { \ + _dbus_warn_check_failed (_dbus_return_if_fail_warning_format, \ + _DBUS_FUNCTION_NAME, #condition, __FILE__, __LINE__); \ + return; \ + } } while (0) + +#define _dbus_return_val_if_fail(condition, val) do { \ + _dbus_assert ((*(const char*)_DBUS_FUNCTION_NAME) != '_'); \ + if (!(condition)) { \ + _dbus_warn_check_failed (_dbus_return_if_fail_warning_format, \ + _DBUS_FUNCTION_NAME, #condition, __FILE__, __LINE__); \ + return (val); \ + } } while (0) + +#endif /* !DBUS_DISABLE_ASSERT */ + +#define _DBUS_N_ELEMENTS(array) ((int) (sizeof ((array)) / sizeof ((array)[0]))) + +#define _DBUS_POINTER_TO_INT(pointer) ((long)(pointer)) +#define _DBUS_INT_TO_POINTER(integer) ((void*)((long)(integer))) + +#define _DBUS_ZERO(object) (memset (&(object), '\0', sizeof ((object)))) + +#define _DBUS_STRUCT_OFFSET(struct_type, member) \ + ((long) ((unsigned char*) &((struct_type*) 0)->member)) + +#ifdef DBUS_DISABLE_CHECKS +/* this is an assert and not an error, but in the typical --disable-checks case (you're trying + * to really minimize code size), disabling these assertions makes sense. + */ +#define _DBUS_ASSERT_ERROR_IS_SET(error) +#define _DBUS_ASSERT_ERROR_IS_CLEAR(error) +#else +#define _DBUS_ASSERT_ERROR_IS_SET(error) _dbus_assert ((error) == NULL || dbus_error_is_set ((error))) +#define _DBUS_ASSERT_ERROR_IS_CLEAR(error) _dbus_assert ((error) == NULL || !dbus_error_is_set ((error))) +#endif + +#define _dbus_return_if_error_is_set(error) _dbus_return_if_fail ((error) == NULL || !dbus_error_is_set ((error))) +#define _dbus_return_val_if_error_is_set(error, val) _dbus_return_val_if_fail ((error) == NULL || !dbus_error_is_set ((error)), (val)) + +/* This alignment thing is from ORBit2 */ +/* Align a value upward to a boundary, expressed as a number of bytes. + * E.g. align to an 8-byte boundary with argument of 8. + */ + +/* + * (this + boundary - 1) + * & + * ~(boundary - 1) + */ + +#define _DBUS_ALIGN_VALUE(this, boundary) \ + (( ((unsigned long)(this)) + (((unsigned long)(boundary)) -1)) & (~(((unsigned long)(boundary))-1))) + +#define _DBUS_ALIGN_ADDRESS(this, boundary) \ + ((void*)_DBUS_ALIGN_VALUE(this, boundary)) + + +char* _dbus_strdup (const char *str); +void* _dbus_memdup (const void *mem, + size_t n_bytes); +dbus_bool_t _dbus_string_array_contains (const char **array, + const char *str); +char** _dbus_dup_string_array (const char **array); + +#define _DBUS_INT16_MIN ((dbus_int16_t) 0x8000) +#define _DBUS_INT16_MAX ((dbus_int16_t) 0x7fff) +#define _DBUS_UINT16_MAX ((dbus_uint16_t)0xffff) +#define _DBUS_INT32_MIN ((dbus_int32_t) 0x80000000) +#define _DBUS_INT32_MAX ((dbus_int32_t) 0x7fffffff) +#define _DBUS_UINT32_MAX ((dbus_uint32_t)0xffffffff) +/* using 32-bit here is sort of bogus */ +#define _DBUS_INT_MIN _DBUS_INT32_MIN +#define _DBUS_INT_MAX _DBUS_INT32_MAX +#define _DBUS_UINT_MAX _DBUS_UINT32_MAX +#ifdef DBUS_HAVE_INT64 +#define _DBUS_INT64_MAX DBUS_INT64_CONSTANT (0x7fffffffffffffff) +#define _DBUS_UINT64_MAX DBUS_UINT64_CONSTANT (0xffffffffffffffff) +#endif +#define _DBUS_ONE_KILOBYTE 1024 +#define _DBUS_ONE_MEGABYTE 1024 * _DBUS_ONE_KILOBYTE +#define _DBUS_ONE_HOUR_IN_MILLISECONDS (1000 * 60 * 60) +#define _DBUS_USEC_PER_SECOND (1000000) + +#undef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +#undef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +#undef ABS +#define ABS(a) (((a) < 0) ? -(a) : (a)) + +#define _DBUS_ISASCII(c) ((c) != '\0' && (((c) & ~0x7f) == 0)) + +typedef void (* DBusForeachFunction) (void *element, + void *data); + +dbus_bool_t _dbus_set_fd_nonblocking (int fd, + DBusError *error); + +void _dbus_verbose_bytes (const unsigned char *data, + int len, + int offset); +void _dbus_verbose_bytes_of_string (const DBusString *str, + int start, + int len); + +const char* _dbus_header_field_to_string (int header_field); + +extern const char _dbus_no_memory_message[]; +#define _DBUS_SET_OOM(error) dbus_set_error_const ((error), DBUS_ERROR_NO_MEMORY, _dbus_no_memory_message) + +#ifdef DBUS_BUILD_TESTS +/* Memory debugging */ +void _dbus_set_fail_alloc_counter (int until_next_fail); +int _dbus_get_fail_alloc_counter (void); +void _dbus_set_fail_alloc_failures (int failures_per_failure); +int _dbus_get_fail_alloc_failures (void); +dbus_bool_t _dbus_decrement_fail_alloc_counter (void); +dbus_bool_t _dbus_disable_mem_pools (void); +int _dbus_get_malloc_blocks_outstanding (void); + +typedef dbus_bool_t (* DBusTestMemoryFunction) (void *data); +dbus_bool_t _dbus_test_oom_handling (const char *description, + DBusTestMemoryFunction func, + void *data); +#else +#define _dbus_set_fail_alloc_counter(n) +#define _dbus_get_fail_alloc_counter _DBUS_INT_MAX + +/* These are constant expressions so that blocks + * they protect should be optimized away + */ +#define _dbus_decrement_fail_alloc_counter() (FALSE) +#define _dbus_disable_mem_pools() (FALSE) +#define _dbus_get_malloc_blocks_outstanding (0) +#endif /* !DBUS_BUILD_TESTS */ + +typedef void (* DBusShutdownFunction) (void *data); +dbus_bool_t _dbus_register_shutdown_func (DBusShutdownFunction function, + void *data); + +extern int _dbus_current_generation; + +/* Thread initializers */ +#define _DBUS_LOCK_NAME(name) _dbus_lock_##name +#define _DBUS_DECLARE_GLOBAL_LOCK(name) extern DBusMutex *_dbus_lock_##name +#define _DBUS_DEFINE_GLOBAL_LOCK(name) DBusMutex *_dbus_lock_##name +#define _DBUS_LOCK(name) _dbus_mutex_lock (_dbus_lock_##name) +#define _DBUS_UNLOCK(name) _dbus_mutex_unlock (_dbus_lock_##name) + +/* 1-5 */ +_DBUS_DECLARE_GLOBAL_LOCK (list); +_DBUS_DECLARE_GLOBAL_LOCK (connection_slots); +_DBUS_DECLARE_GLOBAL_LOCK (pending_call_slots); +_DBUS_DECLARE_GLOBAL_LOCK (server_slots); +_DBUS_DECLARE_GLOBAL_LOCK (message_slots); +/* 5-10 */ +_DBUS_DECLARE_GLOBAL_LOCK (atomic); +_DBUS_DECLARE_GLOBAL_LOCK (bus); +_DBUS_DECLARE_GLOBAL_LOCK (bus_datas); +_DBUS_DECLARE_GLOBAL_LOCK (shutdown_funcs); +_DBUS_DECLARE_GLOBAL_LOCK (system_users); +/* 10-15 */ +_DBUS_DECLARE_GLOBAL_LOCK (message_cache); +_DBUS_DECLARE_GLOBAL_LOCK (shared_connections); +_DBUS_DECLARE_GLOBAL_LOCK (win_fds); +_DBUS_DECLARE_GLOBAL_LOCK (sid_atom_cache); +_DBUS_DECLARE_GLOBAL_LOCK (machine_uuid); +#define _DBUS_N_GLOBAL_LOCKS (15) + +dbus_bool_t _dbus_threads_init_debug (void); + +dbus_bool_t _dbus_address_append_escaped (DBusString *escaped, + const DBusString *unescaped); + +void _dbus_set_bad_address (DBusError *error, + const char *address_problem_type, + const char *address_problem_field, + const char *address_problem_other); + +#define DBUS_UUID_LENGTH_BYTES 16 +#define DBUS_UUID_LENGTH_WORDS (DBUS_UUID_LENGTH_BYTES / 4) +#define DBUS_UUID_LENGTH_HEX (DBUS_UUID_LENGTH_BYTES * 2) + +/** + * A globally unique ID ; we have one for each DBusServer, and also one for each + * machine with libdbus installed on it. + */ +union DBusGUID +{ + dbus_uint32_t as_uint32s[DBUS_UUID_LENGTH_WORDS]; /**< guid as four uint32 values */ + char as_bytes[DBUS_UUID_LENGTH_BYTES]; /**< guid as 16 single-byte values */ +}; + +void _dbus_generate_uuid (DBusGUID *uuid); +dbus_bool_t _dbus_uuid_encode (const DBusGUID *uuid, + DBusString *encoded); +dbus_bool_t _dbus_read_uuid_file (const DBusString *filename, + DBusGUID *uuid, + dbus_bool_t create_if_not_found, + DBusError *error); + +dbus_bool_t _dbus_get_local_machine_uuid_encoded (DBusString *uuid_str); + +DBUS_END_DECLS + +#endif /* DBUS_INTERNALS_H */ diff --git a/src/dbus/dbus-keyring.c b/src/dbus/dbus-keyring.c new file mode 100644 index 0000000..8cc4048 --- /dev/null +++ b/src/dbus/dbus-keyring.c @@ -0,0 +1,1154 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-keyring.c Store secret cookies in your homedir + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-keyring.h" +#include "dbus-protocol.h" +#include +#include +#include + +/** + * @defgroup DBusKeyring keyring class + * @ingroup DBusInternals + * @brief DBusKeyring data structure + * + * Types and functions related to DBusKeyring. DBusKeyring is intended + * to manage cookies used to authenticate clients to servers. This is + * essentially the "verify that client can read the user's homedir" + * authentication mechanism. Both client and server must have access + * to the homedir. + * + * The secret keys are not kept in locked memory, and are written to a + * file in the user's homedir. However they are transient (only used + * by a single server instance for a fixed period of time, then + * discarded). Also, the keys are not sent over the wire. + * + * @todo there's a memory leak on some codepath in here, I saw it once + * when running make check - probably some specific initial cookies + * present in the cookie file, then depending on what we do with them. + */ + +/** + * @defgroup DBusKeyringInternals DBusKeyring implementation details + * @ingroup DBusInternals + * @brief DBusKeyring implementation details + * + * The guts of DBusKeyring. + * + * @{ + */ + +/** The maximum age of a key before we create a new key to use in + * challenges. This isn't super-reliably enforced, since system + * clocks can change or be wrong, but we make a best effort to only + * use keys for a short time. + */ +#define NEW_KEY_TIMEOUT_SECONDS (60*5) +/** + * The time after which we drop a key from the secrets file. + * The EXPIRE_KEYS_TIMEOUT_SECONDS - NEW_KEY_TIMEOUT_SECONDS is the minimum + * time window a client has to complete authentication. + */ +#define EXPIRE_KEYS_TIMEOUT_SECONDS (NEW_KEY_TIMEOUT_SECONDS + (60*2)) +/** + * The maximum amount of time a key can be in the future. + */ +#define MAX_TIME_TRAVEL_SECONDS (60*5) + +/** + * Maximum number of keys in the keyring before + * we just ignore the rest + */ +#ifdef DBUS_BUILD_TESTS +#define MAX_KEYS_IN_FILE 10 +#else +#define MAX_KEYS_IN_FILE 256 +#endif + +/** + * A single key from the cookie file + */ +typedef struct +{ + dbus_int32_t id; /**< identifier used to refer to the key */ + + long creation_time; /**< when the key was generated, + * as unix timestamp. signed long + * matches struct timeval. + */ + + DBusString secret; /**< the actual key */ + +} DBusKey; + +/** + * @brief Internals of DBusKeyring. + * + * DBusKeyring internals. DBusKeyring is an opaque object, it must be + * used via accessor functions. + */ +struct DBusKeyring +{ + int refcount; /**< Reference count */ + DBusString directory; /**< Directory the below two items are inside */ + DBusString filename; /**< Keyring filename */ + DBusString filename_lock; /**< Name of lockfile */ + DBusKey *keys; /**< Keys loaded from the file */ + int n_keys; /**< Number of keys */ + DBusCredentials *credentials; /**< Credentials containing user the keyring is for */ +}; + +static DBusKeyring* +_dbus_keyring_new (void) +{ + DBusKeyring *keyring; + + keyring = dbus_new0 (DBusKeyring, 1); + if (keyring == NULL) + goto out_0; + + if (!_dbus_string_init (&keyring->directory)) + goto out_1; + + if (!_dbus_string_init (&keyring->filename)) + goto out_2; + + if (!_dbus_string_init (&keyring->filename_lock)) + goto out_3; + + keyring->refcount = 1; + keyring->keys = NULL; + keyring->n_keys = 0; + + return keyring; + + /* out_4: */ + _dbus_string_free (&keyring->filename_lock); + out_3: + _dbus_string_free (&keyring->filename); + out_2: + _dbus_string_free (&keyring->directory); + out_1: + dbus_free (keyring); + out_0: + return NULL; +} + +static void +free_keys (DBusKey *keys, + int n_keys) +{ + int i; + + /* should be safe for args NULL, 0 */ + + i = 0; + while (i < n_keys) + { + _dbus_string_free (&keys[i].secret); + ++i; + } + + dbus_free (keys); +} + +/* Our locking scheme is highly unreliable. However, there is + * unfortunately no reliable locking scheme in user home directories; + * between bugs in Linux NFS, people using Tru64 or other total crap + * NFS, AFS, random-file-system-of-the-week, and so forth, fcntl() in + * homedirs simply generates tons of bug reports. This has been + * learned through hard experience with GConf, unfortunately. + * + * This bad hack might work better for the kind of lock we have here, + * which we don't expect to hold for any length of time. Crashing + * while we hold it should be unlikely, and timing out such that we + * delete a stale lock should also be unlikely except when the + * filesystem is running really slowly. Stuff might break in corner + * cases but as long as it's not a security-level breakage it should + * be OK. + */ + +/** Maximum number of timeouts waiting for lock before we decide it's stale */ +#define MAX_LOCK_TIMEOUTS 32 +/** Length of each timeout while waiting for a lock */ +#define LOCK_TIMEOUT_MILLISECONDS 250 + +static dbus_bool_t +_dbus_keyring_lock (DBusKeyring *keyring) +{ + int n_timeouts; + + n_timeouts = 0; + while (n_timeouts < MAX_LOCK_TIMEOUTS) + { + DBusError error = DBUS_ERROR_INIT; + + if (_dbus_create_file_exclusively (&keyring->filename_lock, + &error)) + break; + + _dbus_verbose ("Did not get lock file, sleeping %d milliseconds (%s)\n", + LOCK_TIMEOUT_MILLISECONDS, error.message); + dbus_error_free (&error); + + _dbus_sleep_milliseconds (LOCK_TIMEOUT_MILLISECONDS); + + ++n_timeouts; + } + + if (n_timeouts == MAX_LOCK_TIMEOUTS) + { + DBusError error = DBUS_ERROR_INIT; + + _dbus_verbose ("Lock file timed out %d times, assuming stale\n", + n_timeouts); + + if (!_dbus_delete_file (&keyring->filename_lock, &error)) + { + _dbus_verbose ("Couldn't delete old lock file: %s\n", + error.message); + dbus_error_free (&error); + return FALSE; + } + + if (!_dbus_create_file_exclusively (&keyring->filename_lock, + &error)) + { + _dbus_verbose ("Couldn't create lock file after deleting stale one: %s\n", + error.message); + dbus_error_free (&error); + return FALSE; + } + } + + return TRUE; +} + +static void +_dbus_keyring_unlock (DBusKeyring *keyring) +{ + DBusError error = DBUS_ERROR_INIT; + + if (!_dbus_delete_file (&keyring->filename_lock, &error)) + { + _dbus_warn ("Failed to delete lock file: %s\n", + error.message); + dbus_error_free (&error); + } +} + +static DBusKey* +find_key_by_id (DBusKey *keys, + int n_keys, + int id) +{ + int i; + + i = 0; + while (i < n_keys) + { + if (keys[i].id == id) + return &keys[i]; + + ++i; + } + + return NULL; +} + +static dbus_bool_t +add_new_key (DBusKey **keys_p, + int *n_keys_p, + DBusError *error) +{ + DBusKey *new; + DBusString bytes; + int id; + long timestamp; + const unsigned char *s; + dbus_bool_t retval; + DBusKey *keys; + int n_keys; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + if (!_dbus_string_init (&bytes)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return FALSE; + } + + keys = *keys_p; + n_keys = *n_keys_p; + retval = FALSE; + + /* Generate an integer ID and then the actual key. */ + retry: + + if (!_dbus_generate_random_bytes (&bytes, 4)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto out; + } + + s = (const unsigned char*) _dbus_string_get_const_data (&bytes); + + id = s[0] | (s[1] << 8) | (s[2] << 16) | (s[3] << 24); + if (id < 0) + id = - id; + _dbus_assert (id >= 0); + + if (find_key_by_id (keys, n_keys, id) != NULL) + { + _dbus_string_set_length (&bytes, 0); + _dbus_verbose ("Key ID %d already existed, trying another one\n", + id); + goto retry; + } + + _dbus_verbose ("Creating key with ID %d\n", id); + +#define KEY_LENGTH_BYTES 24 + _dbus_string_set_length (&bytes, 0); + if (!_dbus_generate_random_bytes (&bytes, KEY_LENGTH_BYTES)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto out; + } + + new = dbus_realloc (keys, sizeof (DBusKey) * (n_keys + 1)); + if (new == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto out; + } + + keys = new; + *keys_p = keys; /* otherwise *keys_p ends up invalid */ + n_keys += 1; + + if (!_dbus_string_init (&keys[n_keys-1].secret)) + { + n_keys -= 1; /* we don't want to free the one we didn't init */ + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto out; + } + + _dbus_get_current_time (×tamp, NULL); + + keys[n_keys-1].id = id; + keys[n_keys-1].creation_time = timestamp; + if (!_dbus_string_move (&bytes, 0, + &keys[n_keys-1].secret, + 0)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + _dbus_string_free (&keys[n_keys-1].secret); + n_keys -= 1; + goto out; + } + + retval = TRUE; + + out: + *n_keys_p = n_keys; + + _dbus_string_free (&bytes); + return retval; +} + +/** + * Reloads the keyring file, optionally adds one new key to the file, + * removes all expired keys from the file iff a key was added, then + * resaves the file. Stores the keys from the file in keyring->keys. + * Note that the file is only resaved (written to) if a key is added, + * this means that only servers ever write to the file and need to + * lock it, which avoids a lot of lock contention at login time and + * such. + * + * @param keyring the keyring + * @param add_new #TRUE to add a new key to the file, expire keys, and resave + * @param error return location for errors + * @returns #FALSE on failure + */ +static dbus_bool_t +_dbus_keyring_reload (DBusKeyring *keyring, + dbus_bool_t add_new, + DBusError *error) +{ + DBusString contents; + DBusString line; + dbus_bool_t retval; + dbus_bool_t have_lock; + DBusKey *keys; + int n_keys; + int i; + long now; + DBusError tmp_error; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + if (!_dbus_check_dir_is_private_to_user (&keyring->directory, error)) + return FALSE; + + if (!_dbus_string_init (&contents)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return FALSE; + } + + if (!_dbus_string_init (&line)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + _dbus_string_free (&contents); + return FALSE; + } + + keys = NULL; + n_keys = 0; + retval = FALSE; + have_lock = FALSE; + + _dbus_get_current_time (&now, NULL); + + if (add_new) + { + if (!_dbus_keyring_lock (keyring)) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Could not lock keyring file to add to it"); + goto out; + } + + have_lock = TRUE; + } + + dbus_error_init (&tmp_error); + if (!_dbus_file_get_contents (&contents, + &keyring->filename, + &tmp_error)) + { + _dbus_verbose ("Failed to load keyring file: %s\n", + tmp_error.message); + /* continue with empty keyring file, so we recreate it */ + dbus_error_free (&tmp_error); + } + + if (!_dbus_string_validate_ascii (&contents, 0, + _dbus_string_get_length (&contents))) + { + _dbus_warn ("Secret keyring file contains non-ASCII! Ignoring existing contents\n"); + _dbus_string_set_length (&contents, 0); + } + + /* FIXME this is badly inefficient for large keyring files + * (not that large keyring files exist outside of test suites) + */ + while (_dbus_string_pop_line (&contents, &line)) + { + int next; + long val; + int id; + long timestamp; + int len; + int end; + DBusKey *new; + + /* Don't load more than the max. */ + if (n_keys >= (add_new ? MAX_KEYS_IN_FILE - 1 : MAX_KEYS_IN_FILE)) + break; + + next = 0; + if (!_dbus_string_parse_int (&line, 0, &val, &next)) + { + _dbus_verbose ("could not parse secret key ID at start of line\n"); + continue; + } + + if (val > _DBUS_INT32_MAX || val < 0) + { + _dbus_verbose ("invalid secret key ID at start of line\n"); + continue; + } + + id = val; + + _dbus_string_skip_blank (&line, next, &next); + + if (!_dbus_string_parse_int (&line, next, ×tamp, &next)) + { + _dbus_verbose ("could not parse secret key timestamp\n"); + continue; + } + + if (timestamp < 0 || + (now + MAX_TIME_TRAVEL_SECONDS) < timestamp || + (now - EXPIRE_KEYS_TIMEOUT_SECONDS) > timestamp) + { + _dbus_verbose ("dropping/ignoring %ld-seconds old key with timestamp %ld as current time is %ld\n", + now - timestamp, timestamp, now); + continue; + } + + _dbus_string_skip_blank (&line, next, &next); + + len = _dbus_string_get_length (&line); + + if ((len - next) == 0) + { + _dbus_verbose ("no secret key after ID and timestamp\n"); + continue; + } + + /* We have all three parts */ + new = dbus_realloc (keys, sizeof (DBusKey) * (n_keys + 1)); + if (new == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto out; + } + + keys = new; + n_keys += 1; + + if (!_dbus_string_init (&keys[n_keys-1].secret)) + { + n_keys -= 1; /* we don't want to free the one we didn't init */ + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto out; + } + + keys[n_keys-1].id = id; + keys[n_keys-1].creation_time = timestamp; + if (!_dbus_string_hex_decode (&line, next, &end, + &keys[n_keys-1].secret, 0)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto out; + } + + if (_dbus_string_get_length (&line) != end) + { + _dbus_verbose ("invalid hex encoding in keyring file\n"); + _dbus_string_free (&keys[n_keys - 1].secret); + n_keys -= 1; + continue; + } + } + + _dbus_verbose ("Successfully loaded %d existing keys\n", + n_keys); + + if (add_new) + { + if (!add_new_key (&keys, &n_keys, error)) + { + _dbus_verbose ("Failed to generate new key: %s\n", + error ? error->message : "(unknown)"); + goto out; + } + + _dbus_string_set_length (&contents, 0); + + i = 0; + while (i < n_keys) + { + if (!_dbus_string_append_int (&contents, + keys[i].id)) + goto nomem; + + if (!_dbus_string_append_byte (&contents, ' ')) + goto nomem; + + if (!_dbus_string_append_int (&contents, + keys[i].creation_time)) + goto nomem; + + if (!_dbus_string_append_byte (&contents, ' ')) + goto nomem; + + if (!_dbus_string_hex_encode (&keys[i].secret, 0, + &contents, + _dbus_string_get_length (&contents))) + goto nomem; + + if (!_dbus_string_append_byte (&contents, '\n')) + goto nomem; + + ++i; + continue; + + nomem: + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto out; + } + + if (!_dbus_string_save_to_file (&contents, &keyring->filename, + error)) + goto out; + } + + if (keyring->keys) + free_keys (keyring->keys, keyring->n_keys); + keyring->keys = keys; + keyring->n_keys = n_keys; + keys = NULL; + n_keys = 0; + + retval = TRUE; + + out: + if (have_lock) + _dbus_keyring_unlock (keyring); + + if (! ((retval == TRUE && (error == NULL || error->name == NULL)) || + (retval == FALSE && (error == NULL || error->name != NULL)))) + { + if (error && error->name) + _dbus_verbose ("error is %s: %s\n", error->name, error->message); + _dbus_warn ("returning %d but error pointer %p name %s\n", + retval, error, error->name ? error->name : "(none)"); + _dbus_assert_not_reached ("didn't handle errors properly"); + } + + if (keys != NULL) + { + i = 0; + while (i < n_keys) + { + _dbus_string_zero (&keys[i].secret); + _dbus_string_free (&keys[i].secret); + ++i; + } + + dbus_free (keys); + } + + _dbus_string_free (&contents); + _dbus_string_free (&line); + + return retval; +} + +/** @} */ /* end of internals */ + +/** + * @addtogroup DBusKeyring + * + * @{ + */ + +/** + * Increments reference count of the keyring + * + * @param keyring the keyring + * @returns the keyring + */ +DBusKeyring * +_dbus_keyring_ref (DBusKeyring *keyring) +{ + keyring->refcount += 1; + + return keyring; +} + +/** + * Decrements refcount and finalizes if it reaches + * zero. + * + * @param keyring the keyring + */ +void +_dbus_keyring_unref (DBusKeyring *keyring) +{ + keyring->refcount -= 1; + + if (keyring->refcount == 0) + { + if (keyring->credentials) + _dbus_credentials_unref (keyring->credentials); + + _dbus_string_free (&keyring->filename); + _dbus_string_free (&keyring->filename_lock); + _dbus_string_free (&keyring->directory); + free_keys (keyring->keys, keyring->n_keys); + dbus_free (keyring); + } +} + +/** + * Creates a new keyring that lives in the ~/.dbus-keyrings directory + * of the given user credentials. If the credentials are #NULL or + * empty, uses those of the current process. + * + * @param username username to get keyring for, or #NULL + * @param context which keyring to get + * @param error return location for errors + * @returns the keyring or #NULL on error + */ +DBusKeyring* +_dbus_keyring_new_for_credentials (DBusCredentials *credentials, + const DBusString *context, + DBusError *error) +{ + DBusString ringdir; + DBusKeyring *keyring; + dbus_bool_t error_set; + DBusError tmp_error; + DBusCredentials *our_credentials; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + keyring = NULL; + error_set = FALSE; + our_credentials = NULL; + + if (!_dbus_string_init (&ringdir)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return NULL; + } + + if (credentials != NULL) + { + our_credentials = _dbus_credentials_copy (credentials); + } + else + { + our_credentials = _dbus_credentials_new_from_current_process (); + } + + if (our_credentials == NULL) + goto failed; + + if (_dbus_credentials_are_anonymous (our_credentials)) + { + if (!_dbus_credentials_add_from_current_process (our_credentials)) + goto failed; + } + + if (!_dbus_append_keyring_directory_for_credentials (&ringdir, + our_credentials)) + goto failed; + + keyring = _dbus_keyring_new (); + if (keyring == NULL) + goto failed; + + _dbus_assert (keyring->credentials == NULL); + keyring->credentials = our_credentials; + our_credentials = NULL; /* so we don't unref it again later */ + + /* should have been validated already, but paranoia check here */ + if (!_dbus_keyring_validate_context (context)) + { + error_set = TRUE; + dbus_set_error_const (error, + DBUS_ERROR_FAILED, + "Invalid context in keyring creation"); + goto failed; + } + + /* Save keyring dir in the keyring object */ + if (!_dbus_string_copy (&ringdir, 0, + &keyring->directory, 0)) + goto failed; + + /* Create keyring->filename based on keyring dir and context */ + if (!_dbus_string_copy (&keyring->directory, 0, + &keyring->filename, 0)) + goto failed; + + if (!_dbus_concat_dir_and_file (&keyring->filename, + context)) + goto failed; + + /* Create lockfile name */ + if (!_dbus_string_copy (&keyring->filename, 0, + &keyring->filename_lock, 0)) + goto failed; + + if (!_dbus_string_append (&keyring->filename_lock, ".lock")) + goto failed; + + /* Reload keyring */ + dbus_error_init (&tmp_error); + if (!_dbus_keyring_reload (keyring, FALSE, &tmp_error)) + { + _dbus_verbose ("didn't load an existing keyring: %s\n", + tmp_error.message); + dbus_error_free (&tmp_error); + } + + /* We don't fail fatally if we can't create the directory, + * but the keyring will probably always be empty + * unless someone else manages to create it + */ + dbus_error_init (&tmp_error); + if (!_dbus_create_directory (&keyring->directory, + &tmp_error)) + { + _dbus_verbose ("Creating keyring directory: %s\n", + tmp_error.message); + dbus_error_free (&tmp_error); + } + + _dbus_string_free (&ringdir); + + return keyring; + + failed: + if (!error_set) + dbus_set_error_const (error, + DBUS_ERROR_NO_MEMORY, + NULL); + if (our_credentials) + _dbus_credentials_unref (our_credentials); + if (keyring) + _dbus_keyring_unref (keyring); + _dbus_string_free (&ringdir); + return NULL; + +} + +/** + * Checks whether the context is a valid context. + * Contexts that might cause confusion when used + * in filenames are not allowed (contexts can't + * start with a dot or contain dir separators). + * + * @todo this is the most inefficient implementation + * imaginable. + * + * @param context the context + * @returns #TRUE if valid + */ +dbus_bool_t +_dbus_keyring_validate_context (const DBusString *context) +{ + if (_dbus_string_get_length (context) == 0) + { + _dbus_verbose ("context is zero-length\n"); + return FALSE; + } + + if (!_dbus_string_validate_ascii (context, 0, + _dbus_string_get_length (context))) + { + _dbus_verbose ("context not valid ascii\n"); + return FALSE; + } + + /* no directory separators */ + if (_dbus_string_find (context, 0, "/", NULL)) + { + _dbus_verbose ("context contains a slash\n"); + return FALSE; + } + + if (_dbus_string_find (context, 0, "\\", NULL)) + { + _dbus_verbose ("context contains a backslash\n"); + return FALSE; + } + + /* prevent attempts to use dotfiles or ".." or ".lock" + * all of which might allow some kind of attack + */ + if (_dbus_string_find (context, 0, ".", NULL)) + { + _dbus_verbose ("context contains a dot\n"); + return FALSE; + } + + /* no spaces/tabs, those are used for separators in the protocol */ + if (_dbus_string_find_blank (context, 0, NULL)) + { + _dbus_verbose ("context contains a blank\n"); + return FALSE; + } + + if (_dbus_string_find (context, 0, "\n", NULL)) + { + _dbus_verbose ("context contains a newline\n"); + return FALSE; + } + + if (_dbus_string_find (context, 0, "\r", NULL)) + { + _dbus_verbose ("context contains a carriage return\n"); + return FALSE; + } + + return TRUE; +} + +static DBusKey* +find_recent_key (DBusKeyring *keyring) +{ + int i; + long tv_sec, tv_usec; + + _dbus_get_current_time (&tv_sec, &tv_usec); + + i = 0; + while (i < keyring->n_keys) + { + DBusKey *key = &keyring->keys[i]; + + _dbus_verbose ("Key %d is %ld seconds old\n", + i, tv_sec - key->creation_time); + + if ((tv_sec - NEW_KEY_TIMEOUT_SECONDS) < key->creation_time) + return key; + + ++i; + } + + return NULL; +} + +/** + * Gets a recent key to use for authentication. + * If no recent key exists, creates one. Returns + * the key ID. If a key can't be written to the keyring + * file so no recent key can be created, returns -1. + * All valid keys are > 0. + * + * @param keyring the keyring + * @param error error on failure + * @returns key ID to use for auth, or -1 on failure + */ +int +_dbus_keyring_get_best_key (DBusKeyring *keyring, + DBusError *error) +{ + DBusKey *key; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + key = find_recent_key (keyring); + if (key) + return key->id; + + /* All our keys are too old, or we've never loaded the + * keyring. Create a new one. + */ + if (!_dbus_keyring_reload (keyring, TRUE, + error)) + return -1; + + key = find_recent_key (keyring); + if (key) + return key->id; + else + { + dbus_set_error_const (error, + DBUS_ERROR_FAILED, + "No recent-enough key found in keyring, and unable to create a new key"); + return -1; + } +} + +/** + * Checks whether the keyring is for the same user as the given credentials. + * + * @param keyring the keyring + * @param credentials the credentials to check + * + * @returns #TRUE if the keyring belongs to the given user + */ +dbus_bool_t +_dbus_keyring_is_for_credentials (DBusKeyring *keyring, + DBusCredentials *credentials) +{ + return _dbus_credentials_same_user (keyring->credentials, + credentials); +} + +/** + * Gets the hex-encoded secret key for the given ID. + * Returns #FALSE if not enough memory. Returns #TRUE + * but empty key on any other error such as unknown + * key ID. + * + * @param keyring the keyring + * @param key_id the key ID + * @param hex_key string to append hex-encoded key to + * @returns #TRUE if we had enough memory + */ +dbus_bool_t +_dbus_keyring_get_hex_key (DBusKeyring *keyring, + int key_id, + DBusString *hex_key) +{ + DBusKey *key; + + key = find_key_by_id (keyring->keys, + keyring->n_keys, + key_id); + if (key == NULL) + return TRUE; /* had enough memory, so TRUE */ + + return _dbus_string_hex_encode (&key->secret, 0, + hex_key, + _dbus_string_get_length (hex_key)); +} + +/** @} */ /* end of exposed API */ + +#ifdef DBUS_BUILD_TESTS +#include "dbus-test.h" +#include + +dbus_bool_t +_dbus_keyring_test (void) +{ + DBusString context; + DBusKeyring *ring1; + DBusKeyring *ring2; + int id; + DBusError error; + int i; + + ring1 = NULL; + ring2 = NULL; + + /* Context validation */ + + _dbus_string_init_const (&context, "foo"); + _dbus_assert (_dbus_keyring_validate_context (&context)); + _dbus_string_init_const (&context, "org_freedesktop_blah"); + _dbus_assert (_dbus_keyring_validate_context (&context)); + + _dbus_string_init_const (&context, ""); + _dbus_assert (!_dbus_keyring_validate_context (&context)); + _dbus_string_init_const (&context, ".foo"); + _dbus_assert (!_dbus_keyring_validate_context (&context)); + _dbus_string_init_const (&context, "bar.foo"); + _dbus_assert (!_dbus_keyring_validate_context (&context)); + _dbus_string_init_const (&context, "bar/foo"); + _dbus_assert (!_dbus_keyring_validate_context (&context)); + _dbus_string_init_const (&context, "bar\\foo"); + _dbus_assert (!_dbus_keyring_validate_context (&context)); + _dbus_string_init_const (&context, "foo\xfa\xf0"); + _dbus_assert (!_dbus_keyring_validate_context (&context)); + _dbus_string_init_const (&context, "foo\x80"); + _dbus_assert (!_dbus_keyring_validate_context (&context)); + _dbus_string_init_const (&context, "foo\x7f"); + _dbus_assert (_dbus_keyring_validate_context (&context)); + _dbus_string_init_const (&context, "foo bar"); + _dbus_assert (!_dbus_keyring_validate_context (&context)); + + if (!_dbus_string_init (&context)) + _dbus_assert_not_reached ("no memory"); + if (!_dbus_string_append_byte (&context, '\0')) + _dbus_assert_not_reached ("no memory"); + _dbus_assert (!_dbus_keyring_validate_context (&context)); + _dbus_string_free (&context); + + /* Now verify that if we create a key in keyring 1, + * it is properly loaded in keyring 2 + */ + + _dbus_string_init_const (&context, "org_freedesktop_dbus_testsuite"); + dbus_error_init (&error); + ring1 = _dbus_keyring_new_for_credentials (NULL, &context, + &error); + _dbus_assert (ring1 != NULL); + _dbus_assert (error.name == NULL); + + id = _dbus_keyring_get_best_key (ring1, &error); + if (id < 0) + { + fprintf (stderr, "Could not load keyring: %s\n", error.message); + dbus_error_free (&error); + goto failure; + } + + ring2 = _dbus_keyring_new_for_credentials (NULL, &context, &error); + _dbus_assert (ring2 != NULL); + _dbus_assert (error.name == NULL); + + if (ring1->n_keys != ring2->n_keys) + { + fprintf (stderr, "Different number of keys in keyrings\n"); + goto failure; + } + + /* We guarantee we load and save keeping keys in a fixed + * order + */ + i = 0; + while (i < ring1->n_keys) + { + if (ring1->keys[i].id != ring2->keys[i].id) + { + fprintf (stderr, "Keyring 1 has first key ID %d and keyring 2 has %d\n", + ring1->keys[i].id, ring2->keys[i].id); + goto failure; + } + + if (ring1->keys[i].creation_time != ring2->keys[i].creation_time) + { + fprintf (stderr, "Keyring 1 has first key time %ld and keyring 2 has %ld\n", + ring1->keys[i].creation_time, ring2->keys[i].creation_time); + goto failure; + } + + if (!_dbus_string_equal (&ring1->keys[i].secret, + &ring2->keys[i].secret)) + { + fprintf (stderr, "Keyrings 1 and 2 have different secrets for same ID/timestamp\n"); + goto failure; + } + + ++i; + } + + printf (" %d keys in test\n", ring1->n_keys); + + /* Test ref/unref */ + _dbus_keyring_ref (ring1); + _dbus_keyring_ref (ring2); + _dbus_keyring_unref (ring1); + _dbus_keyring_unref (ring2); + + + /* really unref */ + _dbus_keyring_unref (ring1); + _dbus_keyring_unref (ring2); + + return TRUE; + + failure: + if (ring1) + _dbus_keyring_unref (ring1); + if (ring2) + _dbus_keyring_unref (ring2); + + return FALSE; +} + +#endif /* DBUS_BUILD_TESTS */ + diff --git a/src/dbus/dbus-keyring.h b/src/dbus/dbus-keyring.h new file mode 100644 index 0000000..ed7b3cb --- /dev/null +++ b/src/dbus/dbus-keyring.h @@ -0,0 +1,52 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-keyring.h Store secret cookies in your homedir + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef DBUS_KEYRING_H +#define DBUS_KEYRING_H + +#include +#include +#include +#include + +DBUS_BEGIN_DECLS + +typedef struct DBusKeyring DBusKeyring; + +DBusKeyring* _dbus_keyring_new_for_credentials (DBusCredentials *credentials, + const DBusString *context, + DBusError *error); +DBusKeyring* _dbus_keyring_ref (DBusKeyring *keyring); +void _dbus_keyring_unref (DBusKeyring *keyring); +dbus_bool_t _dbus_keyring_validate_context (const DBusString *context); +int _dbus_keyring_get_best_key (DBusKeyring *keyring, + DBusError *error); +dbus_bool_t _dbus_keyring_is_for_credentials (DBusKeyring *keyring, + DBusCredentials *credentials); +dbus_bool_t _dbus_keyring_get_hex_key (DBusKeyring *keyring, + int key_id, + DBusString *hex_key); + + +DBUS_END_DECLS + +#endif /* DBUS_KEYRING_H */ diff --git a/src/dbus/dbus-list.c b/src/dbus/dbus-list.c new file mode 100644 index 0000000..d314e95 --- /dev/null +++ b/src/dbus/dbus-list.c @@ -0,0 +1,1405 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-list.c Generic linked list utility (internal to D-Bus implementation) + * + * Copyright (C) 2002 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-internals.h" +#include "dbus-list.h" +#include "dbus-mempool.h" +#include "dbus-threads-internal.h" + +/** + * @defgroup DBusList Linked list + * @ingroup DBusInternals + * @brief DBusList data structure + * + * Types and functions related to DBusList. + */ + +static DBusMemPool *list_pool; +_DBUS_DEFINE_GLOBAL_LOCK (list); + +/** + * @defgroup DBusListInternals Linked list implementation details + * @ingroup DBusInternals + * @brief DBusList implementation details + * + * The guts of DBusList. + * + * @{ + */ + +/* the mem pool is probably a speed hit, with the thread + * lock, though it does still save memory - unknown. + */ +static DBusList* +alloc_link (void *data) +{ + DBusList *link; + + _DBUS_LOCK (list); + + if (list_pool == NULL) + { + list_pool = _dbus_mem_pool_new (sizeof (DBusList), TRUE); + + if (list_pool == NULL) + { + _DBUS_UNLOCK (list); + return NULL; + } + + link = _dbus_mem_pool_alloc (list_pool); + if (link == NULL) + { + _dbus_mem_pool_free (list_pool); + list_pool = NULL; + _DBUS_UNLOCK (list); + return NULL; + } + } + else + { + link = _dbus_mem_pool_alloc (list_pool); + } + + if (link) + link->data = data; + + _DBUS_UNLOCK (list); + + return link; +} + +static void +free_link (DBusList *link) +{ + _DBUS_LOCK (list); + if (_dbus_mem_pool_dealloc (list_pool, link)) + { + _dbus_mem_pool_free (list_pool); + list_pool = NULL; + } + + _DBUS_UNLOCK (list); +} + +static void +link_before (DBusList **list, + DBusList *before_this_link, + DBusList *link) +{ + if (*list == NULL) + { + link->prev = link; + link->next = link; + *list = link; + } + else + { + link->next = before_this_link; + link->prev = before_this_link->prev; + before_this_link->prev = link; + link->prev->next = link; + + if (before_this_link == *list) + *list = link; + } +} + +static void +link_after (DBusList **list, + DBusList *after_this_link, + DBusList *link) +{ + if (*list == NULL) + { + link->prev = link; + link->next = link; + *list = link; + } + else + { + link->prev = after_this_link; + link->next = after_this_link->next; + after_this_link->next = link; + link->next->prev = link; + } +} + +/** @} */ + +/** + * @addtogroup DBusList + * @{ + */ + +/** + * @struct DBusList + * + * A node in a linked list. + * + * DBusList is a circular list; that is, the tail of the list + * points back to the head of the list. The empty list is + * represented by a #NULL pointer. + */ + +/** + * @def _dbus_list_get_next_link + * + * Gets the next link in the list, or #NULL if + * there are no more links. Used for iteration. + * + * @code + * DBusList *link; + * link = _dbus_list_get_first_link (&list); + * while (link != NULL) + * { + * printf ("value is %p\n", link->data); + * link = _dbus_list_get_next_link (&link); + * } + * @endcode + * + * @param list address of the list head. + * @param link current link. + * @returns the next link, or %NULL if none. + * + */ + +/** + * @def _dbus_list_get_prev_link + * + * Gets the previous link in the list, or #NULL if + * there are no more links. Used for iteration. + * + * @code + * DBusList *link; + * link = _dbus_list_get_last_link (&list); + * while (link != NULL) + * { + * printf ("value is %p\n", link->data); + * link = _dbus_list_get_prev_link (&link); + * } + * @endcode + * + * @param list address of the list head. + * @param link current link. + * @returns the previous link, or %NULL if none. + * + */ + +/** + * Allocates a linked list node. Useful for preallocating + * nodes and using _dbus_list_append_link() to avoid + * allocations. + * + * @param data the value to store in the link. + * @returns a newly allocated link. + */ +DBusList* +_dbus_list_alloc_link (void *data) +{ + return alloc_link (data); +} + +/** + * Frees a linked list node allocated with _dbus_list_alloc_link. + * Does not free the data in the node. + * + * @param link the list node + */ +void +_dbus_list_free_link (DBusList *link) +{ + free_link (link); +} + + +/** + * Appends a value to the list. May return #FALSE + * if insufficient memory exists to add a list link. + * This is a constant-time operation. + * + * @param list address of the list head. + * @param data the value to append. + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_list_append (DBusList **list, + void *data) +{ + if (!_dbus_list_prepend (list, data)) + return FALSE; + + /* Now cycle the list forward one so the prepended node is the tail */ + *list = (*list)->next; + + return TRUE; +} + +/** + * Prepends a value to the list. May return #FALSE + * if insufficient memory exists to add a list link. + * This is a constant-time operation. + * + * @param list address of the list head. + * @param data the value to prepend. + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_list_prepend (DBusList **list, + void *data) +{ + DBusList *link; + + link = alloc_link (data); + if (link == NULL) + return FALSE; + + link_before (list, *list, link); + + return TRUE; +} + +/** + * Appends a link to the list. + * Cannot fail due to out of memory. + * This is a constant-time operation. + * + * @param list address of the list head. + * @param link the link to append. + */ +void +_dbus_list_append_link (DBusList **list, + DBusList *link) +{ + _dbus_list_prepend_link (list, link); + + /* Now cycle the list forward one so the prepended node is the tail */ + *list = (*list)->next; +} + +/** + * Prepends a link to the list. + * Cannot fail due to out of memory. + * This is a constant-time operation. + * + * @param list address of the list head. + * @param link the link to prepend. + */ +void +_dbus_list_prepend_link (DBusList **list, + DBusList *link) +{ + link_before (list, *list, link); +} + +#ifdef DBUS_BUILD_TESTS +/** + * Inserts data into the list before the given existing link. + * + * @param list the list to modify + * @param before_this_link existing link to insert before, or #NULL to append + * @param data the value to insert + * @returns #TRUE on success, #FALSE if memory allocation fails + */ +dbus_bool_t +_dbus_list_insert_before (DBusList **list, + DBusList *before_this_link, + void *data) +{ + DBusList *link; + + if (before_this_link == NULL) + return _dbus_list_append (list, data); + else + { + link = alloc_link (data); + if (link == NULL) + return FALSE; + + link_before (list, before_this_link, link); + } + + return TRUE; +} +#endif /* DBUS_BUILD_TESTS */ + +/** + * Inserts data into the list after the given existing link. + * + * @param list the list to modify + * @param after_this_link existing link to insert after, or #NULL to prepend + * @param data the value to insert + * @returns #TRUE on success, #FALSE if memory allocation fails + */ +dbus_bool_t +_dbus_list_insert_after (DBusList **list, + DBusList *after_this_link, + void *data) +{ + DBusList *link; + + if (after_this_link == NULL) + return _dbus_list_prepend (list, data); + else + { + link = alloc_link (data); + if (link == NULL) + return FALSE; + + link_after (list, after_this_link, link); + } + + return TRUE; +} + +/** + * Inserts a link into the list before the given existing link. + * + * @param list the list to modify + * @param before_this_link existing link to insert before, or #NULL to append + * @param link the link to insert + */ +void +_dbus_list_insert_before_link (DBusList **list, + DBusList *before_this_link, + DBusList *link) +{ + if (before_this_link == NULL) + _dbus_list_append_link (list, link); + else + link_before (list, before_this_link, link); +} + +/** + * Inserts a link into the list after the given existing link. + * + * @param list the list to modify + * @param after_this_link existing link to insert after, or #NULL to prepend + * @param link the link to insert + */ +void +_dbus_list_insert_after_link (DBusList **list, + DBusList *after_this_link, + DBusList *link) +{ + if (after_this_link == NULL) + _dbus_list_prepend_link (list, link); + else + link_after (list, after_this_link, link); +} + +/** + * Removes a value from the list. Only removes the + * first value equal to the given data pointer, + * even if multiple values exist which match. + * This is a linear-time operation. + * + * @param list address of the list head. + * @param data the value to remove. + * @returns #TRUE if a value was found to remove. + */ +dbus_bool_t +_dbus_list_remove (DBusList **list, + void *data) +{ + DBusList *link; + + link = *list; + while (link != NULL) + { + if (link->data == data) + { + _dbus_list_remove_link (list, link); + return TRUE; + } + + link = _dbus_list_get_next_link (list, link); + } + + return FALSE; +} + +/** + * Removes a value from the list. Only removes the + * last value equal to the given data pointer, + * even if multiple values exist which match. + * This is a linear-time operation. + * + * @param list address of the list head. + * @param data the value to remove. + * @returns #TRUE if a value was found to remove. + */ +dbus_bool_t +_dbus_list_remove_last (DBusList **list, + void *data) +{ + DBusList *link; + + link = _dbus_list_find_last (list, data); + if (link) + { + _dbus_list_remove_link (list, link); + return TRUE; + } + else + return FALSE; +} + +/** + * Finds a value in the list. Returns the last link + * with value equal to the given data pointer. + * This is a linear-time operation. + * Returns #NULL if no value found that matches. + * + * @param list address of the list head. + * @param data the value to find. + * @returns the link if found + */ +DBusList* +_dbus_list_find_last (DBusList **list, + void *data) +{ + DBusList *link; + + link = _dbus_list_get_last_link (list); + + while (link != NULL) + { + if (link->data == data) + return link; + + link = _dbus_list_get_prev_link (list, link); + } + + return NULL; +} + +/** + * Removes the given link from the list, but doesn't + * free it. _dbus_list_remove_link() both removes the + * link and also frees it. + * + * @param list the list + * @param link the link in the list + */ +void +_dbus_list_unlink (DBusList **list, + DBusList *link) +{ + if (link->next == link) + { + /* one-element list */ + *list = NULL; + } + else + { + link->prev->next = link->next; + link->next->prev = link->prev; + + if (*list == link) + *list = link->next; + } + + link->next = NULL; + link->prev = NULL; +} + +/** + * Removes a link from the list. This is a constant-time operation. + * + * @param list address of the list head. + * @param link the list link to remove. + */ +void +_dbus_list_remove_link (DBusList **list, + DBusList *link) +{ + _dbus_list_unlink (list, link); + free_link (link); +} + +/** + * Frees all links in the list and sets the list head to #NULL. Does + * not free the data in each link, for obvious reasons. This is a + * linear-time operation. + * + * @param list address of the list head. + */ +void +_dbus_list_clear (DBusList **list) +{ + DBusList *link; + + link = *list; + while (link != NULL) + { + DBusList *next = _dbus_list_get_next_link (list, link); + + free_link (link); + + link = next; + } + + *list = NULL; +} + +/** + * Gets the first link in the list. + * This is a constant-time operation. + * + * @param list address of the list head. + * @returns the first link, or #NULL for an empty list. + */ +DBusList* +_dbus_list_get_first_link (DBusList **list) +{ + return *list; +} + +/** + * Gets the last link in the list. + * This is a constant-time operation. + * + * @param list address of the list head. + * @returns the last link, or #NULL for an empty list. + */ +DBusList* +_dbus_list_get_last_link (DBusList **list) +{ + if (*list == NULL) + return NULL; + else + return (*list)->prev; +} + +/** + * Gets the last data in the list. + * This is a constant-time operation. + * + * @param list address of the list head. + * @returns the last data in the list, or #NULL for an empty list. + */ +void* +_dbus_list_get_last (DBusList **list) +{ + if (*list == NULL) + return NULL; + else + return (*list)->prev->data; +} + +/** + * Gets the first data in the list. + * This is a constant-time operation. + * + * @param list address of the list head. + * @returns the first data in the list, or #NULL for an empty list. + */ +void* +_dbus_list_get_first (DBusList **list) +{ + if (*list == NULL) + return NULL; + else + return (*list)->data; +} + +/** + * Removes the first link in the list and returns it. This is a + * constant-time operation. + * + * @param list address of the list head. + * @returns the first link in the list, or #NULL for an empty list. + */ +DBusList* +_dbus_list_pop_first_link (DBusList **list) +{ + DBusList *link; + + link = _dbus_list_get_first_link (list); + if (link == NULL) + return NULL; + + _dbus_list_unlink (list, link); + + return link; +} + +/** + * Removes the first value in the list and returns it. This is a + * constant-time operation. + * + * @param list address of the list head. + * @returns the first data in the list, or #NULL for an empty list. + */ +void* +_dbus_list_pop_first (DBusList **list) +{ + DBusList *link; + void *data; + + link = _dbus_list_get_first_link (list); + if (link == NULL) + return NULL; + + data = link->data; + _dbus_list_remove_link (list, link); + + return data; +} + +/** + * Removes the last value in the list and returns it. This is a + * constant-time operation. + * + * @param list address of the list head. + * @returns the last data in the list, or #NULL for an empty list. + */ +void* +_dbus_list_pop_last (DBusList **list) +{ + DBusList *link; + void *data; + + link = _dbus_list_get_last_link (list); + if (link == NULL) + return NULL; + + data = link->data; + _dbus_list_remove_link (list, link); + + return data; +} + +#ifdef DBUS_BUILD_TESTS +/** + * Removes the last link in the list and returns it. This is a + * constant-time operation. + * + * @param list address of the list head. + * @returns the last link in the list, or #NULL for an empty list. + */ +DBusList* +_dbus_list_pop_last_link (DBusList **list) +{ + DBusList *link; + + link = _dbus_list_get_last_link (list); + if (link == NULL) + return NULL; + + _dbus_list_unlink (list, link); + + return link; +} +#endif /* DBUS_BUILD_TESTS */ + +/** + * Copies a list. This is a linear-time operation. If there isn't + * enough memory to copy the entire list, the destination list will be + * set to #NULL. + * + * @param list address of the head of the list to copy. + * @param dest address where the copied list should be placed. + * @returns #TRUE on success, #FALSE if not enough memory. + */ +dbus_bool_t +_dbus_list_copy (DBusList **list, + DBusList **dest) +{ + DBusList *link; + + _dbus_assert (list != dest); + + *dest = NULL; + + link = *list; + while (link != NULL) + { + if (!_dbus_list_append (dest, link->data)) + { + /* free what we have so far */ + _dbus_list_clear (dest); + return FALSE; + } + + link = _dbus_list_get_next_link (list, link); + } + + return TRUE; +} + +/** + * Gets the length of a list. This is a linear-time + * operation. + * + * @param list address of the head of the list + * @returns number of elements in the list. + */ +int +_dbus_list_get_length (DBusList **list) +{ + DBusList *link; + int length; + + length = 0; + + link = *list; + while (link != NULL) + { + ++length; + + link = _dbus_list_get_next_link (list, link); + } + + return length; +} + +/** + * Calls the given function for each element in the list. The + * function is passed the list element as its first argument, and the + * given data as its second argument. + * + * @param list address of the head of the list. + * @param function function to call for each element. + * @param data extra data for the function. + * + */ +void +_dbus_list_foreach (DBusList **list, + DBusForeachFunction function, + void *data) +{ + DBusList *link; + + link = *list; + while (link != NULL) + { + DBusList *next = _dbus_list_get_next_link (list, link); + + (* function) (link->data, data); + + link = next; + } +} + +/** + * Check whether length is exactly one. + * + * @param list the list + * @returns #TRUE if length is exactly one + */ +dbus_bool_t +_dbus_list_length_is_one (DBusList **list) +{ + return (*list != NULL && + (*list)->next == *list); +} + +/** @} */ + +#ifdef DBUS_BUILD_TESTS +#include "dbus-test.h" +#include + +static void +verify_list (DBusList **list) +{ + DBusList *link; + int length; + + link = *list; + + if (link == NULL) + return; + + if (link->next == link) + { + _dbus_assert (link->prev == link); + _dbus_assert (*list == link); + return; + } + + length = 0; + do + { + length += 1; + _dbus_assert (link->prev->next == link); + _dbus_assert (link->next->prev == link); + link = link->next; + } + while (link != *list); + + _dbus_assert (length == _dbus_list_get_length (list)); + + if (length == 1) + _dbus_assert (_dbus_list_length_is_one (list)); + else + _dbus_assert (!_dbus_list_length_is_one (list)); +} + +static dbus_bool_t +is_ascending_sequence (DBusList **list) +{ + DBusList *link; + int prev; + + prev = _DBUS_INT_MIN; + + link = _dbus_list_get_first_link (list); + while (link != NULL) + { + int v = _DBUS_POINTER_TO_INT (link->data); + + if (v <= prev) + return FALSE; + + prev = v; + + link = _dbus_list_get_next_link (list, link); + } + + return TRUE; +} + +static dbus_bool_t +is_descending_sequence (DBusList **list) +{ + DBusList *link; + int prev; + + prev = _DBUS_INT_MAX; + + link = _dbus_list_get_first_link (list); + while (link != NULL) + { + int v = _DBUS_POINTER_TO_INT (link->data); + + if (v >= prev) + return FALSE; + + prev = v; + + link = _dbus_list_get_next_link (list, link); + } + + return TRUE; +} + +static dbus_bool_t +all_even_values (DBusList **list) +{ + DBusList *link; + + link = _dbus_list_get_first_link (list); + while (link != NULL) + { + int v = _DBUS_POINTER_TO_INT (link->data); + + if ((v % 2) != 0) + return FALSE; + + link = _dbus_list_get_next_link (list, link); + } + + return TRUE; +} + +static dbus_bool_t +all_odd_values (DBusList **list) +{ + DBusList *link; + + link = _dbus_list_get_first_link (list); + while (link != NULL) + { + int v = _DBUS_POINTER_TO_INT (link->data); + + if ((v % 2) == 0) + return FALSE; + + link = _dbus_list_get_next_link (list, link); + } + + return TRUE; +} + +static dbus_bool_t +lists_equal (DBusList **list1, + DBusList **list2) +{ + DBusList *link1; + DBusList *link2; + + link1 = _dbus_list_get_first_link (list1); + link2 = _dbus_list_get_first_link (list2); + while (link1 && link2) + { + if (link1->data != link2->data) + return FALSE; + + link1 = _dbus_list_get_next_link (list1, link1); + link2 = _dbus_list_get_next_link (list2, link2); + } + + if (link1 || link2) + return FALSE; + + return TRUE; +} + +/** + * @ingroup DBusListInternals + * Unit test for DBusList + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_list_test (void) +{ + DBusList *list1; + DBusList *list2; + DBusList *link1; + DBusList *link2; + DBusList *copy1; + DBusList *copy2; + int i; + + list1 = NULL; + list2 = NULL; + + /* Test append and prepend */ + + i = 0; + while (i < 10) + { + if (!_dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i))) + _dbus_assert_not_reached ("could not allocate for append"); + + if (!_dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i))) + _dbus_assert_not_reached ("count not allocate for prepend"); + ++i; + + verify_list (&list1); + verify_list (&list2); + + _dbus_assert (_dbus_list_get_length (&list1) == i); + _dbus_assert (_dbus_list_get_length (&list2) == i); + } + + _dbus_assert (is_ascending_sequence (&list1)); + _dbus_assert (is_descending_sequence (&list2)); + + /* Test list clear */ + _dbus_list_clear (&list1); + _dbus_list_clear (&list2); + + verify_list (&list1); + verify_list (&list2); + + /* Test get_first, get_last, pop_first, pop_last */ + + i = 0; + while (i < 10) + { + _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i)); + _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i)); + ++i; + } + + --i; + while (i >= 0) + { + void *got_data1; + void *got_data2; + + void *data1; + void *data2; + + got_data1 = _dbus_list_get_last (&list1); + got_data2 = _dbus_list_get_first (&list2); + + data1 = _dbus_list_pop_last (&list1); + data2 = _dbus_list_pop_first (&list2); + + _dbus_assert (got_data1 == data1); + _dbus_assert (got_data2 == data2); + + _dbus_assert (_DBUS_POINTER_TO_INT (data1) == i); + _dbus_assert (_DBUS_POINTER_TO_INT (data2) == i); + + verify_list (&list1); + verify_list (&list2); + + _dbus_assert (is_ascending_sequence (&list1)); + _dbus_assert (is_descending_sequence (&list2)); + + --i; + } + + _dbus_assert (list1 == NULL); + _dbus_assert (list2 == NULL); + + /* Test get_first_link, get_last_link, pop_first_link, pop_last_link */ + + i = 0; + while (i < 10) + { + _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i)); + _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i)); + ++i; + } + + --i; + while (i >= 0) + { + DBusList *got_link1; + DBusList *got_link2; + + DBusList *link1; + DBusList *link2; + + void *data1; + void *data2; + + got_link1 = _dbus_list_get_last_link (&list1); + got_link2 = _dbus_list_get_first_link (&list2); + + link1 = _dbus_list_pop_last_link (&list1); + link2 = _dbus_list_pop_first_link (&list2); + + _dbus_assert (got_link1 == link1); + _dbus_assert (got_link2 == link2); + + data1 = link1->data; + data2 = link2->data; + + _dbus_list_free_link (link1); + _dbus_list_free_link (link2); + + _dbus_assert (_DBUS_POINTER_TO_INT (data1) == i); + _dbus_assert (_DBUS_POINTER_TO_INT (data2) == i); + + verify_list (&list1); + verify_list (&list2); + + _dbus_assert (is_ascending_sequence (&list1)); + _dbus_assert (is_descending_sequence (&list2)); + + --i; + } + + _dbus_assert (list1 == NULL); + _dbus_assert (list2 == NULL); + + /* Test iteration */ + + i = 0; + while (i < 10) + { + _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i)); + _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i)); + ++i; + + verify_list (&list1); + verify_list (&list2); + + _dbus_assert (_dbus_list_get_length (&list1) == i); + _dbus_assert (_dbus_list_get_length (&list2) == i); + } + + _dbus_assert (is_ascending_sequence (&list1)); + _dbus_assert (is_descending_sequence (&list2)); + + --i; + link2 = _dbus_list_get_first_link (&list2); + while (link2 != NULL) + { + verify_list (&link2); /* pretend this link is the head */ + + _dbus_assert (_DBUS_POINTER_TO_INT (link2->data) == i); + + link2 = _dbus_list_get_next_link (&list2, link2); + --i; + } + + i = 0; + link1 = _dbus_list_get_first_link (&list1); + while (link1 != NULL) + { + verify_list (&link1); /* pretend this link is the head */ + + _dbus_assert (_DBUS_POINTER_TO_INT (link1->data) == i); + + link1 = _dbus_list_get_next_link (&list1, link1); + ++i; + } + + --i; + link1 = _dbus_list_get_last_link (&list1); + while (link1 != NULL) + { + verify_list (&link1); /* pretend this link is the head */ + + _dbus_assert (_DBUS_POINTER_TO_INT (link1->data) == i); + + link1 = _dbus_list_get_prev_link (&list1, link1); + --i; + } + + _dbus_list_clear (&list1); + _dbus_list_clear (&list2); + + /* Test remove */ + + i = 0; + while (i < 10) + { + _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i)); + _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i)); + ++i; + } + + --i; + while (i >= 0) + { + if ((i % 2) == 0) + { + if (!_dbus_list_remove (&list1, _DBUS_INT_TO_POINTER (i))) + _dbus_assert_not_reached ("element should have been in list"); + if (!_dbus_list_remove (&list2, _DBUS_INT_TO_POINTER (i))) + _dbus_assert_not_reached ("element should have been in list"); + + verify_list (&list1); + verify_list (&list2); + } + --i; + } + + _dbus_assert (all_odd_values (&list1)); + _dbus_assert (all_odd_values (&list2)); + + _dbus_list_clear (&list1); + _dbus_list_clear (&list2); + + /* test removing the other half of the elements */ + + i = 0; + while (i < 10) + { + _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i)); + _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i)); + ++i; + } + + --i; + while (i >= 0) + { + if ((i % 2) != 0) + { + if (!_dbus_list_remove (&list1, _DBUS_INT_TO_POINTER (i))) + _dbus_assert_not_reached ("element should have been in list"); + if (!_dbus_list_remove (&list2, _DBUS_INT_TO_POINTER (i))) + _dbus_assert_not_reached ("element should have been in list"); + + verify_list (&list1); + verify_list (&list2); + } + --i; + } + + _dbus_assert (all_even_values (&list1)); + _dbus_assert (all_even_values (&list2)); + + /* clear list using remove_link */ + while (list1 != NULL) + { + _dbus_list_remove_link (&list1, list1); + verify_list (&list1); + } + while (list2 != NULL) + { + _dbus_list_remove_link (&list2, list2); + verify_list (&list2); + } + + /* Test remove link more generally */ + i = 0; + while (i < 10) + { + _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i)); + _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i)); + ++i; + } + + --i; + link2 = _dbus_list_get_first_link (&list2); + while (link2 != NULL) + { + DBusList *next = _dbus_list_get_next_link (&list2, link2); + + _dbus_assert (_DBUS_POINTER_TO_INT (link2->data) == i); + + if ((i % 2) == 0) + _dbus_list_remove_link (&list2, link2); + + verify_list (&list2); + + link2 = next; + --i; + } + + _dbus_assert (all_odd_values (&list2)); + _dbus_list_clear (&list2); + + i = 0; + link1 = _dbus_list_get_first_link (&list1); + while (link1 != NULL) + { + DBusList *next = _dbus_list_get_next_link (&list1, link1); + + _dbus_assert (_DBUS_POINTER_TO_INT (link1->data) == i); + + if ((i % 2) != 0) + _dbus_list_remove_link (&list1, link1); + + verify_list (&list1); + + link1 = next; + ++i; + } + + _dbus_assert (all_even_values (&list1)); + _dbus_list_clear (&list1); + + /* Test copying a list */ + i = 0; + while (i < 10) + { + _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i)); + _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i)); + ++i; + } + + /* bad pointers, because they are allowed in the copy dest */ + copy1 = _DBUS_INT_TO_POINTER (0x342234); + copy2 = _DBUS_INT_TO_POINTER (23); + + _dbus_list_copy (&list1, ©1); + verify_list (&list1); + verify_list (©1); + _dbus_assert (lists_equal (&list1, ©1)); + + _dbus_list_copy (&list2, ©2); + verify_list (&list2); + verify_list (©2); + _dbus_assert (lists_equal (&list2, ©2)); + + /* Now test copying empty lists */ + _dbus_list_clear (&list1); + _dbus_list_clear (&list2); + _dbus_list_clear (©1); + _dbus_list_clear (©2); + + /* bad pointers, because they are allowed in the copy dest */ + copy1 = _DBUS_INT_TO_POINTER (0x342234); + copy2 = _DBUS_INT_TO_POINTER (23); + + _dbus_list_copy (&list1, ©1); + verify_list (&list1); + verify_list (©1); + _dbus_assert (lists_equal (&list1, ©1)); + + _dbus_list_copy (&list2, ©2); + verify_list (&list2); + verify_list (©2); + _dbus_assert (lists_equal (&list2, ©2)); + + _dbus_list_clear (&list1); + _dbus_list_clear (&list2); + + /* insert_before on empty list */ + _dbus_list_insert_before (&list1, NULL, + _DBUS_INT_TO_POINTER (0)); + verify_list (&list1); + + /* inserting before first element */ + _dbus_list_insert_before (&list1, list1, + _DBUS_INT_TO_POINTER (2)); + verify_list (&list1); + _dbus_assert (is_descending_sequence (&list1)); + + /* inserting in the middle */ + _dbus_list_insert_before (&list1, list1->next, + _DBUS_INT_TO_POINTER (1)); + verify_list (&list1); + _dbus_assert (is_descending_sequence (&list1)); + + /* using insert_before to append */ + _dbus_list_insert_before (&list1, NULL, + _DBUS_INT_TO_POINTER (-1)); + verify_list (&list1); + _dbus_assert (is_descending_sequence (&list1)); + + _dbus_list_clear (&list1); + + /* insert_after on empty list */ + _dbus_list_insert_after (&list1, NULL, + _DBUS_INT_TO_POINTER (0)); + verify_list (&list1); + + /* inserting after first element */ + _dbus_list_insert_after (&list1, list1, + _DBUS_INT_TO_POINTER (1)); + verify_list (&list1); + _dbus_assert (is_ascending_sequence (&list1)); + + /* inserting at the end */ + _dbus_list_insert_after (&list1, list1->next, + _DBUS_INT_TO_POINTER (2)); + verify_list (&list1); + _dbus_assert (is_ascending_sequence (&list1)); + + /* using insert_after to prepend */ + _dbus_list_insert_after (&list1, NULL, + _DBUS_INT_TO_POINTER (-1)); + verify_list (&list1); + _dbus_assert (is_ascending_sequence (&list1)); + + _dbus_list_clear (&list1); + + /* using remove_last */ + _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (2)); + _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (1)); + _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (3)); + + _dbus_list_remove_last (&list1, _DBUS_INT_TO_POINTER (2)); + + verify_list (&list1); + _dbus_assert (is_ascending_sequence (&list1)); + + _dbus_list_clear (&list1); + + return TRUE; +} + +#endif diff --git a/src/dbus/dbus-list.h b/src/dbus/dbus-list.h new file mode 100644 index 0000000..69ce265 --- /dev/null +++ b/src/dbus/dbus-list.h @@ -0,0 +1,98 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-list.h Generic linked list utility (internal to D-Bus implementation) + * + * Copyright (C) 2002, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef DBUS_LIST_H +#define DBUS_LIST_H + +#include +#include +#include +#include + +DBUS_BEGIN_DECLS + +struct DBusList +{ + DBusList *prev; /**< Previous list node. */ + DBusList *next; /**< Next list node. */ + void *data; /**< Data stored at this element. */ +}; +dbus_bool_t _dbus_list_append (DBusList **list, + void *data); +dbus_bool_t _dbus_list_prepend (DBusList **list, + void *data); +dbus_bool_t _dbus_list_insert_before (DBusList **list, + DBusList *before_this_link, + void *data); +dbus_bool_t _dbus_list_insert_after (DBusList **list, + DBusList *after_this_link, + void *data); +void _dbus_list_insert_before_link (DBusList **list, + DBusList *before_this_link, + DBusList *link); +void _dbus_list_insert_after_link (DBusList **list, + DBusList *after_this_link, + DBusList *link); +dbus_bool_t _dbus_list_remove (DBusList **list, + void *data); +dbus_bool_t _dbus_list_remove_last (DBusList **list, + void *data); +void _dbus_list_remove_link (DBusList **list, + DBusList *link); +DBusList* _dbus_list_find_last (DBusList **list, + void *data); +void _dbus_list_clear (DBusList **list); +DBusList* _dbus_list_get_first_link (DBusList **list); +DBusList* _dbus_list_get_last_link (DBusList **list); +void* _dbus_list_get_last (DBusList **list); +void* _dbus_list_get_first (DBusList **list); +void* _dbus_list_pop_first (DBusList **list); +void* _dbus_list_pop_last (DBusList **list); +DBusList* _dbus_list_pop_first_link (DBusList **list); +DBusList* _dbus_list_pop_last_link (DBusList **list); +dbus_bool_t _dbus_list_copy (DBusList **list, + DBusList **dest); +int _dbus_list_get_length (DBusList **list); +DBusList* _dbus_list_alloc_link (void *data); +void _dbus_list_free_link (DBusList *link); +void _dbus_list_unlink (DBusList **list, + DBusList *link); +void _dbus_list_append_link (DBusList **list, + DBusList *link); +void _dbus_list_prepend_link (DBusList **list, + DBusList *link); +dbus_bool_t _dbus_list_length_is_one (DBusList **list); + + + + +void _dbus_list_foreach (DBusList **list, + DBusForeachFunction function, + void *data); + +#define _dbus_list_get_next_link(list, link) ((link)->next == *(list) ? NULL : (link)->next) +#define _dbus_list_get_prev_link(list, link) ((link) == *(list) ? NULL : (link)->prev) + +DBUS_END_DECLS + +#endif /* DBUS_LIST_H */ diff --git a/src/dbus/dbus-macros.h b/src/dbus/dbus-macros.h new file mode 100644 index 0000000..bf004b8 --- /dev/null +++ b/src/dbus/dbus-macros.h @@ -0,0 +1,137 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-macros.h generic macros + * + * Copyright (C) 2002 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION) +#error "Only can be included directly, this file may disappear or change contents." +#endif + +#ifndef DBUS_MACROS_H +#define DBUS_MACROS_H + +#ifdef __cplusplus +# define DBUS_BEGIN_DECLS extern "C" { +# define DBUS_END_DECLS } +#else +# define DBUS_BEGIN_DECLS +# define DBUS_END_DECLS +#endif + +#ifndef TRUE +# define TRUE 1 +#endif +#ifndef FALSE +# define FALSE 0 +#endif + +#ifndef NULL +# ifdef __cplusplus +# define NULL (0L) +# else /* !__cplusplus */ +# define NULL ((void*) 0) +# endif /* !__cplusplus */ +#endif + +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) +# define DBUS_DEPRECATED __attribute__ ((__deprecated__)) +#elif defined(_MSC_VER) && (_MSC_VER >= 1300) +# define DBUS_DEPRECATED __declspec(deprecated) +#else +# define DBUS_DEPRECATED +#endif + +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8) +# define _DBUS_GNUC_EXTENSION __extension__ +#else +# define _DBUS_GNUC_EXTENSION +#endif + +/* Normally docs are in .c files, but there isn't a .c file for this. */ +/** + * @defgroup DBusMacros Utility macros + * @ingroup DBus + * @brief #TRUE, #FALSE, #NULL, and so on + * + * Utility macros. + * + * @{ + */ + +/** + * @def DBUS_BEGIN_DECLS + * + * Macro used prior to declaring functions in the D-Bus header + * files. Expands to "extern "C"" when using a C++ compiler, + * and expands to nothing when using a C compiler. + * + * Please don't use this in your own code, consider it + * D-Bus internal. + */ +/** + * @def DBUS_END_DECLS + * + * Macro used after declaring functions in the D-Bus header + * files. Expands to "}" when using a C++ compiler, + * and expands to nothing when using a C compiler. + * + * Please don't use this in your own code, consider it + * D-Bus internal. + */ +/** + * @def TRUE + * + * Expands to "1" + */ +/** + * @def FALSE + * + * Expands to "0" + */ +/** + * @def NULL + * + * A null pointer, defined appropriately for C or C++. + */ +/** + * @def DBUS_DEPRECATED + * + * Tells the compiler to warn about a function or type if it's used. + * Code marked in this way should also be enclosed in + * @code + * #ifndef DBUS_DISABLE_DEPRECATED + * deprecated stuff here + * #endif + * @endcode + * + * Please don't use this in your own code, consider it + * D-Bus internal. + */ +/** + * @def _DBUS_GNUC_EXTENSION + * + * Tells gcc not to warn about extensions to the C standard in the + * following expression, even if compiling with -pedantic. Do not use + * this macro in your own code; please consider it to be internal to libdbus. + */ + +/** @} */ + +#endif /* DBUS_MACROS_H */ diff --git a/src/dbus/dbus-mainloop.c b/src/dbus/dbus-mainloop.c new file mode 100644 index 0000000..ab595af --- /dev/null +++ b/src/dbus/dbus-mainloop.c @@ -0,0 +1,908 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-mainloop.c Main loop utility + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-mainloop.h" + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +#include +#include + +#define MAINLOOP_SPEW 0 + +#if MAINLOOP_SPEW +#ifdef DBUS_ENABLE_VERBOSE_MODE +static const char* +watch_flags_to_string (int flags) +{ + const char *watch_type; + + if ((flags & DBUS_WATCH_READABLE) && + (flags & DBUS_WATCH_WRITABLE)) + watch_type = "readwrite"; + else if (flags & DBUS_WATCH_READABLE) + watch_type = "read"; + else if (flags & DBUS_WATCH_WRITABLE) + watch_type = "write"; + else + watch_type = "not read or write"; + return watch_type; +} +#endif /* DBUS_ENABLE_VERBOSE_MODE */ +#endif /* MAINLOOP_SPEW */ + +struct DBusLoop +{ + int refcount; + DBusList *callbacks; + int callback_list_serial; + int watch_count; + int timeout_count; + int depth; /**< number of recursive runs */ + DBusList *need_dispatch; +}; + +typedef enum +{ + CALLBACK_WATCH, + CALLBACK_TIMEOUT +} CallbackType; + +typedef struct +{ + int refcount; + CallbackType type; + void *data; + DBusFreeFunction free_data_func; +} Callback; + +typedef struct +{ + Callback callback; + DBusWatchFunction function; + DBusWatch *watch; + /* last watch handle failed due to OOM */ + unsigned int last_iteration_oom : 1; +} WatchCallback; + +typedef struct +{ + Callback callback; + DBusTimeout *timeout; + DBusTimeoutFunction function; + unsigned long last_tv_sec; + unsigned long last_tv_usec; +} TimeoutCallback; + +#define WATCH_CALLBACK(callback) ((WatchCallback*)callback) +#define TIMEOUT_CALLBACK(callback) ((TimeoutCallback*)callback) + +static WatchCallback* +watch_callback_new (DBusWatch *watch, + DBusWatchFunction function, + void *data, + DBusFreeFunction free_data_func) +{ + WatchCallback *cb; + + cb = dbus_new (WatchCallback, 1); + if (cb == NULL) + return NULL; + + cb->watch = watch; + cb->function = function; + cb->last_iteration_oom = FALSE; + cb->callback.refcount = 1; + cb->callback.type = CALLBACK_WATCH; + cb->callback.data = data; + cb->callback.free_data_func = free_data_func; + + return cb; +} + +static TimeoutCallback* +timeout_callback_new (DBusTimeout *timeout, + DBusTimeoutFunction function, + void *data, + DBusFreeFunction free_data_func) +{ + TimeoutCallback *cb; + + cb = dbus_new (TimeoutCallback, 1); + if (cb == NULL) + return NULL; + + cb->timeout = timeout; + cb->function = function; + _dbus_get_current_time (&cb->last_tv_sec, + &cb->last_tv_usec); + cb->callback.refcount = 1; + cb->callback.type = CALLBACK_TIMEOUT; + cb->callback.data = data; + cb->callback.free_data_func = free_data_func; + + return cb; +} + +static Callback * +callback_ref (Callback *cb) +{ + _dbus_assert (cb->refcount > 0); + + cb->refcount += 1; + + return cb; +} + +static void +callback_unref (Callback *cb) +{ + _dbus_assert (cb->refcount > 0); + + cb->refcount -= 1; + + if (cb->refcount == 0) + { + if (cb->free_data_func) + (* cb->free_data_func) (cb->data); + + dbus_free (cb); + } +} + +static dbus_bool_t +add_callback (DBusLoop *loop, + Callback *cb) +{ + if (!_dbus_list_append (&loop->callbacks, cb)) + return FALSE; + + loop->callback_list_serial += 1; + + switch (cb->type) + { + case CALLBACK_WATCH: + loop->watch_count += 1; + break; + case CALLBACK_TIMEOUT: + loop->timeout_count += 1; + break; + } + + return TRUE; +} + +static void +remove_callback (DBusLoop *loop, + DBusList *link) +{ + Callback *cb = link->data; + + switch (cb->type) + { + case CALLBACK_WATCH: + loop->watch_count -= 1; + break; + case CALLBACK_TIMEOUT: + loop->timeout_count -= 1; + break; + } + + callback_unref (cb); + _dbus_list_remove_link (&loop->callbacks, link); + loop->callback_list_serial += 1; +} + +DBusLoop* +_dbus_loop_new (void) +{ + DBusLoop *loop; + + loop = dbus_new0 (DBusLoop, 1); + if (loop == NULL) + return NULL; + + loop->refcount = 1; + + return loop; +} + +DBusLoop * +_dbus_loop_ref (DBusLoop *loop) +{ + _dbus_assert (loop != NULL); + _dbus_assert (loop->refcount > 0); + + loop->refcount += 1; + + return loop; +} + +void +_dbus_loop_unref (DBusLoop *loop) +{ + _dbus_assert (loop != NULL); + _dbus_assert (loop->refcount > 0); + + loop->refcount -= 1; + if (loop->refcount == 0) + { + while (loop->need_dispatch) + { + DBusConnection *connection = _dbus_list_pop_first (&loop->need_dispatch); + + dbus_connection_unref (connection); + } + + dbus_free (loop); + } +} + +dbus_bool_t +_dbus_loop_add_watch (DBusLoop *loop, + DBusWatch *watch, + DBusWatchFunction function, + void *data, + DBusFreeFunction free_data_func) +{ + WatchCallback *wcb; + + wcb = watch_callback_new (watch, function, data, free_data_func); + if (wcb == NULL) + return FALSE; + + if (!add_callback (loop, (Callback*) wcb)) + { + wcb->callback.free_data_func = NULL; /* don't want to have this side effect */ + callback_unref ((Callback*) wcb); + return FALSE; + } + + return TRUE; +} + +void +_dbus_loop_remove_watch (DBusLoop *loop, + DBusWatch *watch, + DBusWatchFunction function, + void *data) +{ + DBusList *link; + + link = _dbus_list_get_first_link (&loop->callbacks); + while (link != NULL) + { + DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link); + Callback *this = link->data; + + if (this->type == CALLBACK_WATCH && + WATCH_CALLBACK (this)->watch == watch && + this->data == data && + WATCH_CALLBACK (this)->function == function) + { + remove_callback (loop, link); + + return; + } + + link = next; + } + + _dbus_warn ("could not find watch %p function %p data %p to remove\n", + watch, (void *)function, data); +} + +dbus_bool_t +_dbus_loop_add_timeout (DBusLoop *loop, + DBusTimeout *timeout, + DBusTimeoutFunction function, + void *data, + DBusFreeFunction free_data_func) +{ + TimeoutCallback *tcb; + + tcb = timeout_callback_new (timeout, function, data, free_data_func); + if (tcb == NULL) + return FALSE; + + if (!add_callback (loop, (Callback*) tcb)) + { + tcb->callback.free_data_func = NULL; /* don't want to have this side effect */ + callback_unref ((Callback*) tcb); + return FALSE; + } + + return TRUE; +} + +void +_dbus_loop_remove_timeout (DBusLoop *loop, + DBusTimeout *timeout, + DBusTimeoutFunction function, + void *data) +{ + DBusList *link; + + link = _dbus_list_get_first_link (&loop->callbacks); + while (link != NULL) + { + DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link); + Callback *this = link->data; + + if (this->type == CALLBACK_TIMEOUT && + TIMEOUT_CALLBACK (this)->timeout == timeout && + this->data == data && + TIMEOUT_CALLBACK (this)->function == function) + { + remove_callback (loop, link); + + return; + } + + link = next; + } + + _dbus_warn ("could not find timeout %p function %p data %p to remove\n", + timeout, (void *)function, data); +} + +/* Convolutions from GLib, there really must be a better way + * to do this. + */ +static dbus_bool_t +check_timeout (unsigned long tv_sec, + unsigned long tv_usec, + TimeoutCallback *tcb, + int *timeout) +{ + long sec_remaining; + long msec_remaining; + unsigned long expiration_tv_sec; + unsigned long expiration_tv_usec; + long interval_seconds; + long interval_milliseconds; + int interval; + + /* I'm pretty sure this function could suck (a lot) less */ + + interval = dbus_timeout_get_interval (tcb->timeout); + + interval_seconds = interval / 1000L; + interval_milliseconds = interval % 1000L; + + expiration_tv_sec = tcb->last_tv_sec + interval_seconds; + expiration_tv_usec = tcb->last_tv_usec + interval_milliseconds * 1000; + if (expiration_tv_usec >= 1000000) + { + expiration_tv_usec -= 1000000; + expiration_tv_sec += 1; + } + + sec_remaining = expiration_tv_sec - tv_sec; + /* need to force this to be signed, as it is intended to sometimes + * produce a negative result + */ + msec_remaining = ((long) expiration_tv_usec - (long) tv_usec) / 1000L; + +#if MAINLOOP_SPEW + _dbus_verbose ("Interval is %ld seconds %ld msecs\n", + interval_seconds, + interval_milliseconds); + _dbus_verbose ("Now is %lu seconds %lu usecs\n", + tv_sec, tv_usec); + _dbus_verbose ("Last is %lu seconds %lu usecs\n", + tcb->last_tv_sec, tcb->last_tv_usec); + _dbus_verbose ("Exp is %lu seconds %lu usecs\n", + expiration_tv_sec, expiration_tv_usec); + _dbus_verbose ("Pre-correction, sec_remaining %ld msec_remaining %ld\n", + sec_remaining, msec_remaining); +#endif + + /* We do the following in a rather convoluted fashion to deal with + * the fact that we don't have an integral type big enough to hold + * the difference of two timevals in milliseconds. + */ + if (sec_remaining < 0 || (sec_remaining == 0 && msec_remaining < 0)) + { + *timeout = 0; + } + else + { + if (msec_remaining < 0) + { + msec_remaining += 1000; + sec_remaining -= 1; + } + + if (sec_remaining > (_DBUS_INT_MAX / 1000) || + msec_remaining > _DBUS_INT_MAX) + *timeout = _DBUS_INT_MAX; + else + *timeout = sec_remaining * 1000 + msec_remaining; + } + + if (*timeout > interval) + { + /* This indicates that the system clock probably moved backward */ + _dbus_verbose ("System clock set backward! Resetting timeout.\n"); + + tcb->last_tv_sec = tv_sec; + tcb->last_tv_usec = tv_usec; + + *timeout = interval; + } + +#if MAINLOOP_SPEW + _dbus_verbose (" timeout expires in %d milliseconds\n", *timeout); +#endif + + return *timeout == 0; +} + +dbus_bool_t +_dbus_loop_dispatch (DBusLoop *loop) +{ + +#if MAINLOOP_SPEW + _dbus_verbose (" %d connections to dispatch\n", _dbus_list_get_length (&loop->need_dispatch)); +#endif + + if (loop->need_dispatch == NULL) + return FALSE; + + next: + while (loop->need_dispatch != NULL) + { + DBusConnection *connection = _dbus_list_pop_first (&loop->need_dispatch); + + while (TRUE) + { + DBusDispatchStatus status; + + status = dbus_connection_dispatch (connection); + + if (status == DBUS_DISPATCH_COMPLETE) + { + dbus_connection_unref (connection); + goto next; + } + else + { + if (status == DBUS_DISPATCH_NEED_MEMORY) + _dbus_wait_for_memory (); + } + } + } + + return TRUE; +} + +dbus_bool_t +_dbus_loop_queue_dispatch (DBusLoop *loop, + DBusConnection *connection) +{ + if (_dbus_list_append (&loop->need_dispatch, connection)) + { + dbus_connection_ref (connection); + return TRUE; + } + else + return FALSE; +} + +/* Returns TRUE if we invoked any timeouts or have ready file + * descriptors, which is just used in test code as a debug hack + */ + +dbus_bool_t +_dbus_loop_iterate (DBusLoop *loop, + dbus_bool_t block) +{ +#define N_STACK_DESCRIPTORS 64 + dbus_bool_t retval; + DBusPollFD *fds; + DBusPollFD stack_fds[N_STACK_DESCRIPTORS]; + int n_fds; + WatchCallback **watches_for_fds; + WatchCallback *stack_watches_for_fds[N_STACK_DESCRIPTORS]; + int i; + DBusList *link; + int n_ready; + int initial_serial; + long timeout; + dbus_bool_t oom_watch_pending; + int orig_depth; + + retval = FALSE; + + fds = NULL; + watches_for_fds = NULL; + n_fds = 0; + oom_watch_pending = FALSE; + orig_depth = loop->depth; + +#if MAINLOOP_SPEW + _dbus_verbose ("Iteration block=%d depth=%d timeout_count=%d watch_count=%d\n", + block, loop->depth, loop->timeout_count, loop->watch_count); +#endif + + if (loop->callbacks == NULL) + goto next_iteration; + + if (loop->watch_count > N_STACK_DESCRIPTORS) + { + fds = dbus_new0 (DBusPollFD, loop->watch_count); + + while (fds == NULL) + { + _dbus_wait_for_memory (); + fds = dbus_new0 (DBusPollFD, loop->watch_count); + } + + watches_for_fds = dbus_new (WatchCallback*, loop->watch_count); + while (watches_for_fds == NULL) + { + _dbus_wait_for_memory (); + watches_for_fds = dbus_new (WatchCallback*, loop->watch_count); + } + } + else + { + fds = stack_fds; + watches_for_fds = stack_watches_for_fds; + } + + /* fill our array of fds and watches */ + n_fds = 0; + link = _dbus_list_get_first_link (&loop->callbacks); + while (link != NULL) + { + DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link); + Callback *cb = link->data; + if (cb->type == CALLBACK_WATCH) + { + unsigned int flags; + WatchCallback *wcb = WATCH_CALLBACK (cb); + + if (wcb->last_iteration_oom) + { + /* we skip this one this time, but reenable it next time, + * and have a timeout on this iteration + */ + wcb->last_iteration_oom = FALSE; + oom_watch_pending = TRUE; + + retval = TRUE; /* return TRUE here to keep the loop going, + * since we don't know the watch is inactive + */ + +#if MAINLOOP_SPEW + _dbus_verbose (" skipping watch on fd %d as it was out of memory last time\n", + dbus_watch_get_socket (wcb->watch)); +#endif + } + else if (dbus_watch_get_enabled (wcb->watch)) + { + watches_for_fds[n_fds] = wcb; + + callback_ref (cb); + + flags = dbus_watch_get_flags (wcb->watch); + + fds[n_fds].fd = dbus_watch_get_socket (wcb->watch); + fds[n_fds].revents = 0; + fds[n_fds].events = 0; + if (flags & DBUS_WATCH_READABLE) + fds[n_fds].events |= _DBUS_POLLIN; + if (flags & DBUS_WATCH_WRITABLE) + fds[n_fds].events |= _DBUS_POLLOUT; + +#if MAINLOOP_SPEW + _dbus_verbose (" polling watch on fd %d %s\n", + fds[n_fds].fd, watch_flags_to_string (flags)); +#endif + + n_fds += 1; + } + else + { +#if MAINLOOP_SPEW + _dbus_verbose (" skipping disabled watch on fd %d %s\n", + dbus_watch_get_socket (wcb->watch), + watch_flags_to_string (dbus_watch_get_flags (wcb->watch))); +#endif + } + } + + link = next; + } + + timeout = -1; + if (loop->timeout_count > 0) + { + unsigned long tv_sec; + unsigned long tv_usec; + + _dbus_get_current_time (&tv_sec, &tv_usec); + + link = _dbus_list_get_first_link (&loop->callbacks); + while (link != NULL) + { + DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link); + Callback *cb = link->data; + + if (cb->type == CALLBACK_TIMEOUT && + dbus_timeout_get_enabled (TIMEOUT_CALLBACK (cb)->timeout)) + { + TimeoutCallback *tcb = TIMEOUT_CALLBACK (cb); + int msecs_remaining; + + check_timeout (tv_sec, tv_usec, tcb, &msecs_remaining); + + if (timeout < 0) + timeout = msecs_remaining; + else + timeout = MIN (msecs_remaining, timeout); + +#if MAINLOOP_SPEW + _dbus_verbose (" timeout added, %d remaining, aggregate timeout %ld\n", + msecs_remaining, timeout); +#endif + + _dbus_assert (timeout >= 0); + + if (timeout == 0) + break; /* it's not going to get shorter... */ + } +#if MAINLOOP_SPEW + else if (cb->type == CALLBACK_TIMEOUT) + { + _dbus_verbose (" skipping disabled timeout\n"); + } +#endif + + link = next; + } + } + + /* Never block if we have stuff to dispatch */ + if (!block || loop->need_dispatch != NULL) + { + timeout = 0; +#if MAINLOOP_SPEW + _dbus_verbose (" timeout is 0 as we aren't blocking\n"); +#endif + } + + /* if a watch is OOM, don't wait longer than the OOM + * wait to re-enable it + */ + if (oom_watch_pending) + timeout = MIN (timeout, _dbus_get_oom_wait ()); + +#if MAINLOOP_SPEW + _dbus_verbose (" polling on %d descriptors timeout %ld\n", n_fds, timeout); +#endif + + n_ready = _dbus_poll (fds, n_fds, timeout); + + initial_serial = loop->callback_list_serial; + + if (loop->timeout_count > 0) + { + unsigned long tv_sec; + unsigned long tv_usec; + + _dbus_get_current_time (&tv_sec, &tv_usec); + + /* It'd be nice to avoid this O(n) thingy here */ + link = _dbus_list_get_first_link (&loop->callbacks); + while (link != NULL) + { + DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link); + Callback *cb = link->data; + + if (initial_serial != loop->callback_list_serial) + goto next_iteration; + + if (loop->depth != orig_depth) + goto next_iteration; + + if (cb->type == CALLBACK_TIMEOUT && + dbus_timeout_get_enabled (TIMEOUT_CALLBACK (cb)->timeout)) + { + TimeoutCallback *tcb = TIMEOUT_CALLBACK (cb); + int msecs_remaining; + + if (check_timeout (tv_sec, tv_usec, + tcb, &msecs_remaining)) + { + /* Save last callback time and fire this timeout */ + tcb->last_tv_sec = tv_sec; + tcb->last_tv_usec = tv_usec; + +#if MAINLOOP_SPEW + _dbus_verbose (" invoking timeout\n"); +#endif + + (* tcb->function) (tcb->timeout, + cb->data); + + retval = TRUE; + } + else + { +#if MAINLOOP_SPEW + _dbus_verbose (" timeout has not expired\n"); +#endif + } + } +#if MAINLOOP_SPEW + else if (cb->type == CALLBACK_TIMEOUT) + { + _dbus_verbose (" skipping invocation of disabled timeout\n"); + } +#endif + + link = next; + } + } + + if (n_ready > 0) + { + i = 0; + while (i < n_fds) + { + /* FIXME I think this "restart if we change the watches" + * approach could result in starving watches + * toward the end of the list. + */ + if (initial_serial != loop->callback_list_serial) + goto next_iteration; + + if (loop->depth != orig_depth) + goto next_iteration; + + if (fds[i].revents != 0) + { + WatchCallback *wcb; + unsigned int condition; + + wcb = watches_for_fds[i]; + + condition = 0; + if (fds[i].revents & _DBUS_POLLIN) + condition |= DBUS_WATCH_READABLE; + if (fds[i].revents & _DBUS_POLLOUT) + condition |= DBUS_WATCH_WRITABLE; + if (fds[i].revents & _DBUS_POLLHUP) + condition |= DBUS_WATCH_HANGUP; + if (fds[i].revents & _DBUS_POLLERR) + condition |= DBUS_WATCH_ERROR; + + /* condition may still be 0 if we got some + * weird POLLFOO thing like POLLWRBAND + */ + + if (condition != 0 && + dbus_watch_get_enabled (wcb->watch)) + { + if (!(* wcb->function) (wcb->watch, + condition, + ((Callback*)wcb)->data)) + wcb->last_iteration_oom = TRUE; + +#if MAINLOOP_SPEW + _dbus_verbose (" Invoked watch, oom = %d\n", + wcb->last_iteration_oom); +#endif + + retval = TRUE; + } + } + + ++i; + } + } + + next_iteration: +#if MAINLOOP_SPEW + _dbus_verbose (" moving to next iteration\n"); +#endif + + if (fds && fds != stack_fds) + dbus_free (fds); + if (watches_for_fds) + { + i = 0; + while (i < n_fds) + { + callback_unref (&watches_for_fds[i]->callback); + ++i; + } + + if (watches_for_fds != stack_watches_for_fds) + dbus_free (watches_for_fds); + } + + if (_dbus_loop_dispatch (loop)) + retval = TRUE; + +#if MAINLOOP_SPEW + _dbus_verbose ("Returning %d\n", retval); +#endif + + return retval; +} + +void +_dbus_loop_run (DBusLoop *loop) +{ + int our_exit_depth; + + _dbus_assert (loop->depth >= 0); + + _dbus_loop_ref (loop); + + our_exit_depth = loop->depth; + loop->depth += 1; + + _dbus_verbose ("Running main loop, depth %d -> %d\n", + loop->depth - 1, loop->depth); + + while (loop->depth != our_exit_depth) + _dbus_loop_iterate (loop, TRUE); + + _dbus_loop_unref (loop); +} + +void +_dbus_loop_quit (DBusLoop *loop) +{ + _dbus_assert (loop->depth > 0); + + loop->depth -= 1; + + _dbus_verbose ("Quit main loop, depth %d -> %d\n", + loop->depth + 1, loop->depth); +} + +int +_dbus_get_oom_wait (void) +{ +#ifdef DBUS_BUILD_TESTS + /* make tests go fast */ + return 0; +#else + return 500; +#endif +} + +void +_dbus_wait_for_memory (void) +{ + _dbus_verbose ("Waiting for more memory\n"); + _dbus_sleep_milliseconds (_dbus_get_oom_wait ()); +} + +#endif /* !DOXYGEN_SHOULD_SKIP_THIS */ diff --git a/src/dbus/dbus-mainloop.h b/src/dbus/dbus-mainloop.h new file mode 100644 index 0000000..4a3f9ec --- /dev/null +++ b/src/dbus/dbus-mainloop.h @@ -0,0 +1,76 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-mainloop.h Main loop utility + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef DBUS_MAINLOOP_H +#define DBUS_MAINLOOP_H + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +#include + +typedef struct DBusLoop DBusLoop; + +typedef dbus_bool_t (* DBusWatchFunction) (DBusWatch *watch, + unsigned int condition, + void *data); +typedef void (* DBusTimeoutFunction) (DBusTimeout *timeout, + void *data); + +DBusLoop* _dbus_loop_new (void); +DBusLoop* _dbus_loop_ref (DBusLoop *loop); +void _dbus_loop_unref (DBusLoop *loop); +dbus_bool_t _dbus_loop_add_watch (DBusLoop *loop, + DBusWatch *watch, + DBusWatchFunction function, + void *data, + DBusFreeFunction free_data_func); +void _dbus_loop_remove_watch (DBusLoop *loop, + DBusWatch *watch, + DBusWatchFunction function, + void *data); +dbus_bool_t _dbus_loop_add_timeout (DBusLoop *loop, + DBusTimeout *timeout, + DBusTimeoutFunction function, + void *data, + DBusFreeFunction free_data_func); +void _dbus_loop_remove_timeout (DBusLoop *loop, + DBusTimeout *timeout, + DBusTimeoutFunction function, + void *data); + +dbus_bool_t _dbus_loop_queue_dispatch (DBusLoop *loop, + DBusConnection *connection); + +void _dbus_loop_run (DBusLoop *loop); +void _dbus_loop_quit (DBusLoop *loop); +dbus_bool_t _dbus_loop_iterate (DBusLoop *loop, + dbus_bool_t block); +dbus_bool_t _dbus_loop_dispatch (DBusLoop *loop); + +int _dbus_get_oom_wait (void); +void _dbus_wait_for_memory (void); + +#endif /* !DOXYGEN_SHOULD_SKIP_THIS */ + +#endif /* DBUS_MAINLOOP_H */ + diff --git a/src/dbus/dbus-marshal-basic.c b/src/dbus/dbus-marshal-basic.c new file mode 100644 index 0000000..38fbe2d --- /dev/null +++ b/src/dbus/dbus-marshal-basic.c @@ -0,0 +1,1988 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-marshal-basic.c Marshalling routines for basic (primitive) types + * + * Copyright (C) 2002 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-internals.h" +#include "dbus-marshal-basic.h" +#include "dbus-signature.h" + +#include + +/** + * @defgroup DBusMarshal marshaling and unmarshaling + * @ingroup DBusInternals + * @brief functions to marshal/unmarshal data from the wire + * + * Types and functions related to converting primitive data types from + * wire format to native machine format, and vice versa. + * + * A signature is just a string with multiple types one after the other. + * for example a type is "i" or "(ii)", a signature is "i(ii)" + * where i is int and (ii) is struct { int; int; } + * + * @{ + */ + +static void +pack_2_octets (dbus_uint16_t value, + int byte_order, + unsigned char *data) +{ + _dbus_assert (_DBUS_ALIGN_ADDRESS (data, 2) == data); + + if ((byte_order) == DBUS_LITTLE_ENDIAN) + *((dbus_uint16_t*)(data)) = DBUS_UINT16_TO_LE (value); + else + *((dbus_uint16_t*)(data)) = DBUS_UINT16_TO_BE (value); +} + +static void +pack_4_octets (dbus_uint32_t value, + int byte_order, + unsigned char *data) +{ + _dbus_assert (_DBUS_ALIGN_ADDRESS (data, 4) == data); + + if ((byte_order) == DBUS_LITTLE_ENDIAN) + *((dbus_uint32_t*)(data)) = DBUS_UINT32_TO_LE (value); + else + *((dbus_uint32_t*)(data)) = DBUS_UINT32_TO_BE (value); +} + +static void +pack_8_octets (DBusBasicValue value, + int byte_order, + unsigned char *data) +{ + _dbus_assert (_DBUS_ALIGN_ADDRESS (data, 8) == data); + +#ifdef DBUS_HAVE_INT64 + if ((byte_order) == DBUS_LITTLE_ENDIAN) + *((dbus_uint64_t*)(data)) = DBUS_UINT64_TO_LE (value.u64); + else + *((dbus_uint64_t*)(data)) = DBUS_UINT64_TO_BE (value.u64); +#else + *(DBus8ByteStruct*)data = value.u64; + swap_8_octets ((DBusBasicValue*)data, byte_order); +#endif +} + +/** + * Packs a 32 bit unsigned integer into a data pointer. + * + * @param value the value + * @param byte_order the byte order to use + * @param data the data pointer + */ +void +_dbus_pack_uint32 (dbus_uint32_t value, + int byte_order, + unsigned char *data) +{ + pack_4_octets (value, byte_order, data); +} + +#ifndef DBUS_HAVE_INT64 +/* from ORBit */ +static void +swap_bytes (unsigned char *data, + unsigned int len) +{ + unsigned char *p1 = data; + unsigned char *p2 = data + len - 1; + + while (p1 < p2) + { + unsigned char tmp = *p1; + *p1 = *p2; + *p2 = tmp; + + --p2; + ++p1; + } +} +#endif /* !DBUS_HAVE_INT64 */ + +static void +swap_8_octets (DBusBasicValue *value, + int byte_order) +{ + if (byte_order != DBUS_COMPILER_BYTE_ORDER) + { +#ifdef DBUS_HAVE_INT64 + value->u64 = DBUS_UINT64_SWAP_LE_BE (value->u64); +#else + swap_bytes ((unsigned char *)value, 8); +#endif + } +} + +#if 0 +static DBusBasicValue +unpack_8_octets (int byte_order, + const unsigned char *data) +{ + DBusBasicValue r; + + _dbus_assert (_DBUS_ALIGN_ADDRESS (data, 8) == data); + _dbus_assert (sizeof (r) == 8); + +#ifdef DBUS_HAVE_INT64 + if (byte_order == DBUS_LITTLE_ENDIAN) + r.u64 = DBUS_UINT64_FROM_LE (*(dbus_uint64_t*)data); + else + r.u64 = DBUS_UINT64_FROM_BE (*(dbus_uint64_t*)data); +#else + r.u64 = *(DBus8ByteStruct*)data; + swap_8_octets (&r, byte_order); +#endif + + return r; +} +#endif + +#ifndef _dbus_unpack_uint16 +/** + * Unpacks a 16 bit unsigned integer from a data pointer + * + * @param byte_order The byte order to use + * @param data the data pointer + * @returns the integer + */ +dbus_uint16_t +_dbus_unpack_uint16 (int byte_order, + const unsigned char *data) +{ + _dbus_assert (_DBUS_ALIGN_ADDRESS (data, 2) == data); + + if (byte_order == DBUS_LITTLE_ENDIAN) + return DBUS_UINT16_FROM_LE (*(dbus_uint16_t*)data); + else + return DBUS_UINT16_FROM_BE (*(dbus_uint16_t*)data); +} +#endif /* _dbus_unpack_uint16 */ + +#ifndef _dbus_unpack_uint32 +/** + * Unpacks a 32 bit unsigned integer from a data pointer + * + * @param byte_order The byte order to use + * @param data the data pointer + * @returns the integer + */ +dbus_uint32_t +_dbus_unpack_uint32 (int byte_order, + const unsigned char *data) +{ + _dbus_assert (_DBUS_ALIGN_ADDRESS (data, 4) == data); + + if (byte_order == DBUS_LITTLE_ENDIAN) + return DBUS_UINT32_FROM_LE (*(dbus_uint32_t*)data); + else + return DBUS_UINT32_FROM_BE (*(dbus_uint32_t*)data); +} +#endif /* _dbus_unpack_uint32 */ + +static void +set_2_octets (DBusString *str, + int offset, + dbus_uint16_t value, + int byte_order) +{ + char *data; + + _dbus_assert (byte_order == DBUS_LITTLE_ENDIAN || + byte_order == DBUS_BIG_ENDIAN); + + data = _dbus_string_get_data_len (str, offset, 2); + + pack_2_octets (value, byte_order, data); +} + +static void +set_4_octets (DBusString *str, + int offset, + dbus_uint32_t value, + int byte_order) +{ + char *data; + + _dbus_assert (byte_order == DBUS_LITTLE_ENDIAN || + byte_order == DBUS_BIG_ENDIAN); + + data = _dbus_string_get_data_len (str, offset, 4); + + pack_4_octets (value, byte_order, data); +} + +static void +set_8_octets (DBusString *str, + int offset, + DBusBasicValue value, + int byte_order) +{ + char *data; + + _dbus_assert (byte_order == DBUS_LITTLE_ENDIAN || + byte_order == DBUS_BIG_ENDIAN); + + data = _dbus_string_get_data_len (str, offset, 8); + + pack_8_octets (value, byte_order, data); +} + +/** + * Sets the 4 bytes at the given offset to a marshaled unsigned + * integer, replacing anything found there previously. + * + * @param str the string to write the marshalled int to + * @param pos the byte offset where int should be written + * @param value the value + * @param byte_order the byte order to use + * + */ +void +_dbus_marshal_set_uint32 (DBusString *str, + int pos, + dbus_uint32_t value, + int byte_order) +{ + set_4_octets (str, pos, value, byte_order); +} + +/** + * Sets the existing marshaled string at the given offset with + * a new marshaled string. The given offset must point to + * an existing string or the wrong length will be deleted + * and replaced with the new string. + * + * Note: no attempt is made by this function to re-align + * any data which has been already marshalled after this + * string. Use with caution. + * + * @param str the string to write the marshalled string to + * @param pos the position of the marshaled string length + * @param value the value + * @param byte_order the byte order to use + * @param old_end_pos place to store byte after the nul byte of the old value + * @param new_end_pos place to store byte after the nul byte of the new value + * @returns #TRUE on success, #FALSE if no memory + * + */ +static dbus_bool_t +set_string (DBusString *str, + int pos, + const char *value, + int byte_order, + int *old_end_pos, + int *new_end_pos) +{ + int old_len, new_len; + DBusString dstr; + + _dbus_string_init_const (&dstr, value); + + _dbus_assert (_DBUS_ALIGN_VALUE (pos, 4) == (unsigned) pos); + old_len = _dbus_unpack_uint32 (byte_order, + _dbus_string_get_const_data_len (str, pos, 4)); + + new_len = _dbus_string_get_length (&dstr); + + if (!_dbus_string_replace_len (&dstr, 0, new_len, + str, pos + 4, old_len)) + return FALSE; + + _dbus_marshal_set_uint32 (str, pos, new_len, byte_order); + + if (old_end_pos) + *old_end_pos = pos + 4 + old_len + 1; + if (new_end_pos) + *new_end_pos = pos + 4 + new_len + 1; + + return TRUE; +} + +/** + * Sets the existing marshaled signature at the given offset to a new + * marshaled signature. Same basic ideas as set_string(). + * + * @param str the string to write the marshalled signature to + * @param pos the position of the marshaled signature length + * @param value the value + * @param byte_order the byte order to use + * @param old_end_pos place to store byte after the nul byte of the old value + * @param new_end_pos place to store byte after the nul byte of the new value + * @returns #TRUE on success, #FALSE if no memory + * + */ +static dbus_bool_t +set_signature (DBusString *str, + int pos, + const char *value, + int byte_order, + int *old_end_pos, + int *new_end_pos) +{ + int old_len, new_len; + DBusString dstr; + + _dbus_string_init_const (&dstr, value); + + old_len = _dbus_string_get_byte (str, pos); + new_len = _dbus_string_get_length (&dstr); + + if (!_dbus_string_replace_len (&dstr, 0, new_len, + str, pos + 1, old_len)) + return FALSE; + + _dbus_string_set_byte (str, pos, new_len); + + if (old_end_pos) + *old_end_pos = pos + 1 + old_len + 1; + if (new_end_pos) + *new_end_pos = pos + 1 + new_len + 1; + + return TRUE; +} + +/** + * Sets an existing basic type value to a new value. + * Arguments work the same way as _dbus_marshal_basic_type(). + * + * @param str the string + * @param pos location of the current value + * @param type the type of the current and new values + * @param value the address of the new value + * @param byte_order byte order for marshaling + * @param old_end_pos location to store end position of the old value, or #NULL + * @param new_end_pos location to store end position of the new value, or #NULL + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_marshal_set_basic (DBusString *str, + int pos, + int type, + const void *value, + int byte_order, + int *old_end_pos, + int *new_end_pos) +{ + const DBusBasicValue *vp; + + vp = value; + + switch (type) + { + case DBUS_TYPE_BYTE: + _dbus_string_set_byte (str, pos, vp->byt); + if (old_end_pos) + *old_end_pos = pos + 1; + if (new_end_pos) + *new_end_pos = pos + 1; + return TRUE; + break; + case DBUS_TYPE_INT16: + case DBUS_TYPE_UINT16: + pos = _DBUS_ALIGN_VALUE (pos, 2); + set_2_octets (str, pos, vp->u16, byte_order); + if (old_end_pos) + *old_end_pos = pos + 2; + if (new_end_pos) + *new_end_pos = pos + 2; + return TRUE; + break; + case DBUS_TYPE_BOOLEAN: + case DBUS_TYPE_INT32: + case DBUS_TYPE_UINT32: + pos = _DBUS_ALIGN_VALUE (pos, 4); + set_4_octets (str, pos, vp->u32, byte_order); + if (old_end_pos) + *old_end_pos = pos + 4; + if (new_end_pos) + *new_end_pos = pos + 4; + return TRUE; + break; + case DBUS_TYPE_INT64: + case DBUS_TYPE_UINT64: + case DBUS_TYPE_DOUBLE: + pos = _DBUS_ALIGN_VALUE (pos, 8); + set_8_octets (str, pos, *vp, byte_order); + if (old_end_pos) + *old_end_pos = pos + 8; + if (new_end_pos) + *new_end_pos = pos + 8; + return TRUE; + break; + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + pos = _DBUS_ALIGN_VALUE (pos, 4); + _dbus_assert (vp->str != NULL); + return set_string (str, pos, vp->str, byte_order, + old_end_pos, new_end_pos); + break; + case DBUS_TYPE_SIGNATURE: + _dbus_assert (vp->str != NULL); + return set_signature (str, pos, vp->str, byte_order, + old_end_pos, new_end_pos); + break; + default: + _dbus_assert_not_reached ("not a basic type"); + return FALSE; + break; + } +} + +/** + * Convenience function to demarshal a 32 bit unsigned integer. + * + * @param str the string containing the data + * @param byte_order the byte order + * @param pos the position in the string + * @param new_pos the new position of the string + * @returns the demarshaled integer. + */ +dbus_uint32_t +_dbus_marshal_read_uint32 (const DBusString *str, + int pos, + int byte_order, + int *new_pos) +{ + pos = _DBUS_ALIGN_VALUE (pos, 4); + + if (new_pos) + *new_pos = pos + 4; + + _dbus_assert (pos + 4 <= _dbus_string_get_length (str)); + + return _dbus_unpack_uint32 (byte_order, + _dbus_string_get_const_data (str) + pos); +} + +/** + * Demarshals a basic-typed value. The "value" pointer is always + * the address of a variable of the basic type. So e.g. + * if the basic type is "double" then the pointer is + * a double*, and if it's "char*" then the pointer is + * a "char**". + * + * A value of type #DBusBasicValue is guaranteed to be large enough to + * hold any of the types that may be returned, which is handy if you + * are trying to do things generically. For example you can pass + * a DBusBasicValue* in to this function, and then pass the same + * DBusBasicValue* in to _dbus_marshal_basic_type() in order to + * move a value from one place to another. + * + * @param str the string containing the data + * @param pos position in the string + * @param type type of value to demarshal + * @param value pointer to return value data + * @param byte_order the byte order + * @param new_pos pointer to update with new position, or #NULL + **/ +void +_dbus_marshal_read_basic (const DBusString *str, + int pos, + int type, + void *value, + int byte_order, + int *new_pos) +{ + const char *str_data; + + _dbus_assert (dbus_type_is_basic (type)); + + str_data = _dbus_string_get_const_data (str); + + /* Below we volatile types to avoid aliasing issues; + * see http://bugs.freedesktop.org/show_bug.cgi?id=20137 + */ + + switch (type) + { + case DBUS_TYPE_BYTE: + { + volatile unsigned char *vp = value; + *vp = (unsigned char) _dbus_string_get_byte (str, pos); + (pos)++; + } + break; + case DBUS_TYPE_INT16: + case DBUS_TYPE_UINT16: + { + volatile dbus_uint16_t *vp = value; + pos = _DBUS_ALIGN_VALUE (pos, 2); + *vp = *(dbus_uint16_t *)(str_data + pos); + if (byte_order != DBUS_COMPILER_BYTE_ORDER) + *vp = DBUS_UINT16_SWAP_LE_BE (*vp); + pos += 2; + } + break; + case DBUS_TYPE_INT32: + case DBUS_TYPE_UINT32: + case DBUS_TYPE_BOOLEAN: + { + volatile dbus_uint32_t *vp = value; + pos = _DBUS_ALIGN_VALUE (pos, 4); + *vp = *(dbus_uint32_t *)(str_data + pos); + if (byte_order != DBUS_COMPILER_BYTE_ORDER) + *vp = DBUS_UINT32_SWAP_LE_BE (*vp); + pos += 4; + } + break; + case DBUS_TYPE_INT64: + case DBUS_TYPE_UINT64: + case DBUS_TYPE_DOUBLE: + { + volatile dbus_uint64_t *vp = value; + pos = _DBUS_ALIGN_VALUE (pos, 8); +#ifdef DBUS_HAVE_INT64 + if (byte_order != DBUS_COMPILER_BYTE_ORDER) + *vp = DBUS_UINT64_SWAP_LE_BE (*(dbus_uint64_t*)(str_data + pos)); + else + *vp = *(dbus_uint64_t*)(str_data + pos); +#else + *vp = *(DBus8ByteStruct*) (str_data + pos); + swap_8_octets (vp, byte_order); +#endif + pos += 8; + } + break; + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + { + int len; + volatile char **vp = value; + + len = _dbus_marshal_read_uint32 (str, pos, byte_order, &pos); + + *vp = (char*) str_data + pos; + + pos += len + 1; /* length plus nul */ + } + break; + case DBUS_TYPE_SIGNATURE: + { + int len; + volatile char **vp = value; + + len = _dbus_string_get_byte (str, pos); + pos += 1; + + *vp = (char*) str_data + pos; + + pos += len + 1; /* length plus nul */ + } + break; + default: + _dbus_warn_check_failed ("type %s %d not a basic type\n", + _dbus_type_to_string (type), type); + _dbus_assert_not_reached ("not a basic type"); + break; + } + + if (new_pos) + *new_pos = pos; +} + +static dbus_bool_t +marshal_2_octets (DBusString *str, + int insert_at, + dbus_uint16_t value, + int byte_order, + int *pos_after) +{ + dbus_bool_t retval; + int orig_len; + + _dbus_assert (sizeof (value) == 2); + + if (byte_order != DBUS_COMPILER_BYTE_ORDER) + value = DBUS_UINT16_SWAP_LE_BE (value); + + orig_len = _dbus_string_get_length (str); + + retval = _dbus_string_insert_2_aligned (str, insert_at, + (const unsigned char *)&value); + + if (pos_after) + { + *pos_after = insert_at + (_dbus_string_get_length (str) - orig_len); + _dbus_assert (*pos_after <= _dbus_string_get_length (str)); + } + + return retval; +} + +static dbus_bool_t +marshal_4_octets (DBusString *str, + int insert_at, + dbus_uint32_t value, + int byte_order, + int *pos_after) +{ + dbus_bool_t retval; + int orig_len; + + _dbus_assert (sizeof (value) == 4); + + if (byte_order != DBUS_COMPILER_BYTE_ORDER) + value = DBUS_UINT32_SWAP_LE_BE (value); + + orig_len = _dbus_string_get_length (str); + + retval = _dbus_string_insert_4_aligned (str, insert_at, + (const unsigned char *)&value); + + if (pos_after) + { + *pos_after = insert_at + (_dbus_string_get_length (str) - orig_len); + _dbus_assert (*pos_after <= _dbus_string_get_length (str)); + } + + return retval; +} + +static dbus_bool_t +marshal_8_octets (DBusString *str, + int insert_at, + DBusBasicValue value, + int byte_order, + int *pos_after) +{ + dbus_bool_t retval; + int orig_len; + + _dbus_assert (sizeof (value) == 8); + + swap_8_octets (&value, byte_order); + + orig_len = _dbus_string_get_length (str); + + retval = _dbus_string_insert_8_aligned (str, insert_at, + (const unsigned char *)&value); + + if (pos_after) + *pos_after = insert_at + _dbus_string_get_length (str) - orig_len; + + return retval; +} + +enum + { + MARSHAL_AS_STRING, + MARSHAL_AS_SIGNATURE, + MARSHAL_AS_BYTE_ARRAY + }; + +static dbus_bool_t +marshal_len_followed_by_bytes (int marshal_as, + DBusString *str, + int insert_at, + const unsigned char *value, + int data_len, /* doesn't include nul if any */ + int byte_order, + int *pos_after) +{ + int pos; + DBusString value_str; + int value_len; + + _dbus_assert (byte_order == DBUS_LITTLE_ENDIAN || byte_order == DBUS_BIG_ENDIAN); + if (insert_at > _dbus_string_get_length (str)) + _dbus_warn ("insert_at = %d string len = %d data_len = %d\n", + insert_at, _dbus_string_get_length (str), data_len); + + if (marshal_as == MARSHAL_AS_BYTE_ARRAY) + value_len = data_len; + else + value_len = data_len + 1; /* value has a nul */ + + _dbus_string_init_const_len (&value_str, value, value_len); + + pos = insert_at; + + if (marshal_as == MARSHAL_AS_SIGNATURE) + { + _dbus_assert (data_len <= DBUS_MAXIMUM_SIGNATURE_LENGTH); + _dbus_assert (data_len <= 255); /* same as max sig len right now */ + + if (!_dbus_string_insert_byte (str, pos, data_len)) + goto oom; + + pos += 1; + } + else + { + if (!marshal_4_octets (str, pos, data_len, + byte_order, &pos)) + goto oom; + } + + if (!_dbus_string_copy_len (&value_str, 0, value_len, + str, pos)) + goto oom; + +#if 0 + /* too expensive */ + _dbus_assert (_dbus_string_equal_substring (&value_str, 0, value_len, + str, pos)); + _dbus_verbose_bytes_of_string (str, pos, value_len); +#endif + + pos += value_len; + + if (pos_after) + *pos_after = pos; + + return TRUE; + + oom: + /* Delete what we've inserted */ + _dbus_string_delete (str, insert_at, pos - insert_at); + + return FALSE; +} + +static dbus_bool_t +marshal_string (DBusString *str, + int insert_at, + const char *value, + int byte_order, + int *pos_after) +{ + return marshal_len_followed_by_bytes (MARSHAL_AS_STRING, + str, insert_at, value, + strlen (value), + byte_order, pos_after); +} + +static dbus_bool_t +marshal_signature (DBusString *str, + int insert_at, + const char *value, + int *pos_after) +{ + return marshal_len_followed_by_bytes (MARSHAL_AS_SIGNATURE, + str, insert_at, value, + strlen (value), + DBUS_COMPILER_BYTE_ORDER, /* irrelevant */ + pos_after); +} + +/** + * Marshals a basic-typed value. The "value" pointer is always the + * address of a variable containing the basic type value. + * So for example for int32 it will be dbus_int32_t*, and + * for string it will be const char**. This is for symmetry + * with _dbus_marshal_read_basic() and to have a simple + * consistent rule. + * + * @param str string to marshal to + * @param insert_at where to insert the value + * @param type type of value + * @param value pointer to a variable containing the value + * @param byte_order byte order + * @param pos_after #NULL or the position after the type + * @returns #TRUE on success + **/ +dbus_bool_t +_dbus_marshal_write_basic (DBusString *str, + int insert_at, + int type, + const void *value, + int byte_order, + int *pos_after) +{ + const DBusBasicValue *vp; + + _dbus_assert (dbus_type_is_basic (type)); + + vp = value; + + switch (type) + { + case DBUS_TYPE_BYTE: + if (!_dbus_string_insert_byte (str, insert_at, vp->byt)) + return FALSE; + if (pos_after) + *pos_after = insert_at + 1; + return TRUE; + break; + case DBUS_TYPE_INT16: + case DBUS_TYPE_UINT16: + return marshal_2_octets (str, insert_at, vp->u16, + byte_order, pos_after); + break; + case DBUS_TYPE_BOOLEAN: + return marshal_4_octets (str, insert_at, vp->u32 != FALSE, + byte_order, pos_after); + break; + case DBUS_TYPE_INT32: + case DBUS_TYPE_UINT32: + return marshal_4_octets (str, insert_at, vp->u32, + byte_order, pos_after); + break; + case DBUS_TYPE_INT64: + case DBUS_TYPE_UINT64: + case DBUS_TYPE_DOUBLE: + return marshal_8_octets (str, insert_at, *vp, byte_order, pos_after); + break; + + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + _dbus_assert (vp->str != NULL); + return marshal_string (str, insert_at, vp->str, byte_order, pos_after); + break; + case DBUS_TYPE_SIGNATURE: + _dbus_assert (vp->str != NULL); + return marshal_signature (str, insert_at, vp->str, pos_after); + break; + default: + _dbus_assert_not_reached ("not a basic type"); + return FALSE; + break; + } +} + +static dbus_bool_t +marshal_1_octets_array (DBusString *str, + int insert_at, + const unsigned char *value, + int n_elements, + int byte_order, + int *pos_after) +{ + int pos; + DBusString value_str; + + _dbus_string_init_const_len (&value_str, value, n_elements); + + pos = insert_at; + + if (!_dbus_string_copy_len (&value_str, 0, n_elements, + str, pos)) + return FALSE; + + pos += n_elements; + + if (pos_after) + *pos_after = pos; + + return TRUE; +} + +/** + * Swaps the elements of an array to the opposite byte order + * + * @param data start of array + * @param n_elements number of elements + * @param alignment size of each element + */ +void +_dbus_swap_array (unsigned char *data, + int n_elements, + int alignment) +{ + unsigned char *d; + unsigned char *end; + + _dbus_assert (_DBUS_ALIGN_ADDRESS (data, alignment) == data); + + /* we use const_data and cast it off so DBusString can be a const string + * for the unit tests. don't ask. + */ + d = data; + end = d + (n_elements * alignment); + + if (alignment == 8) + { + while (d != end) + { +#ifdef DBUS_HAVE_INT64 + *((dbus_uint64_t*)d) = DBUS_UINT64_SWAP_LE_BE (*((dbus_uint64_t*)d)); +#else + swap_8_bytes ((DBusBasicValue*) d); +#endif + d += 8; + } + } + else if (alignment == 4) + { + while (d != end) + { + *((dbus_uint32_t*)d) = DBUS_UINT32_SWAP_LE_BE (*((dbus_uint32_t*)d)); + d += 4; + } + } + else + { + _dbus_assert (alignment == 2); + + while (d != end) + { + *((dbus_uint16_t*)d) = DBUS_UINT16_SWAP_LE_BE (*((dbus_uint16_t*)d)); + d += 2; + } + } +} + +static void +swap_array (DBusString *str, + int array_start, + int n_elements, + int byte_order, + int alignment) +{ + _dbus_assert (_DBUS_ALIGN_VALUE (array_start, alignment) == (unsigned) array_start); + + if (byte_order != DBUS_COMPILER_BYTE_ORDER) + { + /* we use const_data and cast it off so DBusString can be a const string + * for the unit tests. don't ask. + */ + _dbus_swap_array ((unsigned char*) (_dbus_string_get_const_data (str) + array_start), + n_elements, alignment); + } +} + +static dbus_bool_t +marshal_fixed_multi (DBusString *str, + int insert_at, + const DBusBasicValue *value, + int n_elements, + int byte_order, + int alignment, + int *pos_after) +{ + int old_string_len; + int array_start; + DBusString t; + int len_in_bytes; + + _dbus_assert (n_elements <= DBUS_MAXIMUM_ARRAY_LENGTH / alignment); + + old_string_len = _dbus_string_get_length (str); + + len_in_bytes = n_elements * alignment; + array_start = insert_at; + + /* Note that we do alignment padding unconditionally + * even if the array is empty; this means that + * padding + len is always equal to the number of bytes + * in the array. + */ + + if (!_dbus_string_insert_alignment (str, &array_start, alignment)) + goto error; + + _dbus_string_init_const_len (&t, + (const unsigned char*) value, + len_in_bytes); + + if (!_dbus_string_copy (&t, 0, + str, array_start)) + goto error; + + swap_array (str, array_start, n_elements, byte_order, alignment); + + if (pos_after) + *pos_after = array_start + len_in_bytes; + + return TRUE; + + error: + _dbus_string_delete (str, insert_at, + _dbus_string_get_length (str) - old_string_len); + + return FALSE; +} + +/** + * Marshals a block of values of fixed-length type all at once, as an + * optimization. dbus_type_is_fixed() returns #TRUE for fixed-length + * types, which are the basic types minus the string-like types. + * + * The value argument should be the adddress of an + * array, so e.g. "const dbus_uint32_t**" + * + * @param str string to marshal to + * @param insert_at where to insert the value + * @param element_type type of array elements + * @param value address of an array to marshal + * @param n_elements number of elements in the array + * @param byte_order byte order + * @param pos_after #NULL or the position after the type + * @returns #TRUE on success + **/ +dbus_bool_t +_dbus_marshal_write_fixed_multi (DBusString *str, + int insert_at, + int element_type, + const void *value, + int n_elements, + int byte_order, + int *pos_after) +{ + const void* vp = *(const DBusBasicValue**)value; + + _dbus_assert (dbus_type_is_fixed (element_type)); + _dbus_assert (n_elements >= 0); + +#if 0 + _dbus_verbose ("writing %d elements of %s\n", + n_elements, _dbus_type_to_string (element_type)); +#endif + + switch (element_type) + { + case DBUS_TYPE_BYTE: + return marshal_1_octets_array (str, insert_at, vp, n_elements, byte_order, pos_after); + break; + case DBUS_TYPE_INT16: + case DBUS_TYPE_UINT16: + return marshal_fixed_multi (str, insert_at, vp, n_elements, byte_order, 2, pos_after); + /* FIXME: we canonicalize to 0 or 1 for the single boolean case + * should we here too ? */ + case DBUS_TYPE_BOOLEAN: + case DBUS_TYPE_INT32: + case DBUS_TYPE_UINT32: + return marshal_fixed_multi (str, insert_at, vp, n_elements, byte_order, 4, pos_after); + break; + case DBUS_TYPE_INT64: + case DBUS_TYPE_UINT64: + case DBUS_TYPE_DOUBLE: + return marshal_fixed_multi (str, insert_at, vp, n_elements, byte_order, 8, pos_after); + break; + + default: + _dbus_assert_not_reached ("non fixed type in array write"); + break; + } + + return FALSE; +} + + +/** + * Skips over a basic-typed value, reporting the following position. + * + * @param str the string containing the data + * @param type type of value to read + * @param byte_order the byte order + * @param pos pointer to position in the string, + * updated on return to new position + **/ +void +_dbus_marshal_skip_basic (const DBusString *str, + int type, + int byte_order, + int *pos) +{ + _dbus_assert (byte_order == DBUS_LITTLE_ENDIAN || + byte_order == DBUS_BIG_ENDIAN); + + switch (type) + { + case DBUS_TYPE_BYTE: + (*pos)++; + break; + case DBUS_TYPE_INT16: + case DBUS_TYPE_UINT16: + *pos = _DBUS_ALIGN_VALUE (*pos, 2); + *pos += 2; + break; + case DBUS_TYPE_BOOLEAN: + case DBUS_TYPE_INT32: + case DBUS_TYPE_UINT32: + *pos = _DBUS_ALIGN_VALUE (*pos, 4); + *pos += 4; + break; + case DBUS_TYPE_INT64: + case DBUS_TYPE_UINT64: + case DBUS_TYPE_DOUBLE: + *pos = _DBUS_ALIGN_VALUE (*pos, 8); + *pos += 8; + break; + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + { + int len; + + len = _dbus_marshal_read_uint32 (str, *pos, byte_order, pos); + + *pos += len + 1; /* length plus nul */ + } + break; + case DBUS_TYPE_SIGNATURE: + { + int len; + + len = _dbus_string_get_byte (str, *pos); + + *pos += len + 2; /* length byte plus length plus nul */ + } + break; + default: + _dbus_warn ("type %s not a basic type\n", + _dbus_type_to_string (type)); + _dbus_assert_not_reached ("not a basic type"); + break; + } +} + +/** + * Skips an array, returning the next position. + * + * @param str the string containing the data + * @param element_type the type of array elements + * @param byte_order the byte order + * @param pos pointer to position in the string, + * updated on return to new position + */ +void +_dbus_marshal_skip_array (const DBusString *str, + int element_type, + int byte_order, + int *pos) +{ + dbus_uint32_t array_len; + int i; + int alignment; + + i = _DBUS_ALIGN_VALUE (*pos, 4); + + array_len = _dbus_marshal_read_uint32 (str, i, byte_order, &i); + + alignment = _dbus_type_get_alignment (element_type); + + i = _DBUS_ALIGN_VALUE (i, alignment); + + *pos = i + array_len; +} + +/** + * Gets the alignment requirement for the given type; + * will be 1, 4, or 8. + * + * @param typecode the type + * @returns alignment of 1, 4, or 8 + */ +int +_dbus_type_get_alignment (int typecode) +{ + switch (typecode) + { + case DBUS_TYPE_BYTE: + case DBUS_TYPE_VARIANT: + case DBUS_TYPE_SIGNATURE: + return 1; + case DBUS_TYPE_INT16: + case DBUS_TYPE_UINT16: + return 2; + case DBUS_TYPE_BOOLEAN: + case DBUS_TYPE_INT32: + case DBUS_TYPE_UINT32: + /* this stuff is 4 since it starts with a length */ + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + case DBUS_TYPE_ARRAY: + return 4; + case DBUS_TYPE_INT64: + case DBUS_TYPE_UINT64: + case DBUS_TYPE_DOUBLE: + /* struct is 8 since it could contain an 8-aligned item + * and it's simpler to just always align structs to 8; + * we want the amount of padding in a struct of a given + * type to be predictable, not location-dependent. + * DICT_ENTRY is always the same as struct. + */ + case DBUS_TYPE_STRUCT: + case DBUS_TYPE_DICT_ENTRY: + return 8; + + default: + _dbus_assert_not_reached ("unknown typecode in _dbus_type_get_alignment()"); + return 0; + } +} + + +/** + * Return #TRUE if the typecode is a valid typecode. + * #DBUS_TYPE_INVALID surprisingly enough is not considered valid, and + * random unknown bytes aren't either. This function is safe with + * untrusted data. + * + * @returns #TRUE if valid + */ +dbus_bool_t +_dbus_type_is_valid (int typecode) +{ + switch (typecode) + { + case DBUS_TYPE_BYTE: + case DBUS_TYPE_BOOLEAN: + case DBUS_TYPE_INT16: + case DBUS_TYPE_UINT16: + case DBUS_TYPE_INT32: + case DBUS_TYPE_UINT32: + case DBUS_TYPE_INT64: + case DBUS_TYPE_UINT64: + case DBUS_TYPE_DOUBLE: + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + case DBUS_TYPE_SIGNATURE: + case DBUS_TYPE_ARRAY: + case DBUS_TYPE_STRUCT: + case DBUS_TYPE_DICT_ENTRY: + case DBUS_TYPE_VARIANT: + return TRUE; + + default: + return FALSE; + } +} + +/** + * Returns a string describing the given type. + * + * @param typecode the type to describe + * @returns a constant string describing the type + */ +const char * +_dbus_type_to_string (int typecode) +{ + switch (typecode) + { + case DBUS_TYPE_INVALID: + return "invalid"; + case DBUS_TYPE_BOOLEAN: + return "boolean"; + case DBUS_TYPE_BYTE: + return "byte"; + case DBUS_TYPE_INT16: + return "int16"; + case DBUS_TYPE_UINT16: + return "uint16"; + case DBUS_TYPE_INT32: + return "int32"; + case DBUS_TYPE_UINT32: + return "uint32"; + case DBUS_TYPE_INT64: + return "int64"; + case DBUS_TYPE_UINT64: + return "uint64"; + case DBUS_TYPE_DOUBLE: + return "double"; + case DBUS_TYPE_STRING: + return "string"; + case DBUS_TYPE_OBJECT_PATH: + return "object_path"; + case DBUS_TYPE_SIGNATURE: + return "signature"; + case DBUS_TYPE_STRUCT: + return "struct"; + case DBUS_TYPE_DICT_ENTRY: + return "dict_entry"; + case DBUS_TYPE_ARRAY: + return "array"; + case DBUS_TYPE_VARIANT: + return "variant"; + case DBUS_STRUCT_BEGIN_CHAR: + return "begin_struct"; + case DBUS_STRUCT_END_CHAR: + return "end_struct"; + case DBUS_DICT_ENTRY_BEGIN_CHAR: + return "begin_dict_entry"; + case DBUS_DICT_ENTRY_END_CHAR: + return "end_dict_entry"; + default: + return "unknown"; + } +} + +/** + * If in verbose mode, print a block of binary data. + * + * @param data the data + * @param len the length of the data + * @param offset where to start counting for byte indexes + */ +void +_dbus_verbose_bytes (const unsigned char *data, + int len, + int offset) +{ + int i; + const unsigned char *aligned; + + _dbus_assert (len >= 0); + + if (!_dbus_is_verbose()) + return; + + /* Print blanks on first row if appropriate */ + aligned = _DBUS_ALIGN_ADDRESS (data, 4); + if (aligned > data) + aligned -= 4; + _dbus_assert (aligned <= data); + + if (aligned != data) + { + _dbus_verbose ("%4ld\t%p: ", - (long)(data - aligned), aligned); + while (aligned != data) + { + _dbus_verbose (" "); + ++aligned; + } + } + + /* now print the bytes */ + i = 0; + while (i < len) + { + if (_DBUS_ALIGN_ADDRESS (&data[i], 4) == &data[i]) + { + _dbus_verbose ("%4d\t%p: ", + offset + i, &data[i]); + } + + if (data[i] >= 32 && + data[i] <= 126) + _dbus_verbose (" '%c' ", data[i]); + else + _dbus_verbose ("0x%s%x ", + data[i] <= 0xf ? "0" : "", data[i]); + + ++i; + + if (_DBUS_ALIGN_ADDRESS (&data[i], 4) == &data[i]) + { + if (i > 3) + _dbus_verbose ("BE: %d LE: %d", + _dbus_unpack_uint32 (DBUS_BIG_ENDIAN, &data[i-4]), + _dbus_unpack_uint32 (DBUS_LITTLE_ENDIAN, &data[i-4])); + + if (i > 7 && + _DBUS_ALIGN_ADDRESS (&data[i], 8) == &data[i]) + { +#ifdef DBUS_HAVE_INT64 + /* I think I probably mean "GNU libc printf" and not "GNUC" + * but we'll wait until someone complains. If you hit this, + * just turn off verbose mode as a workaround. + */ +#if __GNUC__ + _dbus_verbose (" u64: 0x%llx", + *(dbus_uint64_t*)&data[i-8]); +#endif +#endif + _dbus_verbose (" dbl: %g", + *(double*)&data[i-8]); + } + + _dbus_verbose ("\n"); + } + } + + _dbus_verbose ("\n"); +} + +/** + * Dump the given part of the string to verbose log. + * + * @param str the string + * @param start the start of range to dump + * @param len length of range + */ +void +_dbus_verbose_bytes_of_string (const DBusString *str, + int start, + int len) +{ + const char *d; + int real_len; + + real_len = _dbus_string_get_length (str); + + _dbus_assert (start >= 0); + + if (start > real_len) + { + _dbus_verbose (" [%d,%d) is not inside string of length %d\n", + start, len, real_len); + return; + } + + if ((start + len) > real_len) + { + _dbus_verbose (" [%d,%d) extends outside string of length %d\n", + start, len, real_len); + len = real_len - start; + } + + d = _dbus_string_get_const_data_len (str, start, len); + + _dbus_verbose_bytes (d, len, start); +} + +static int +map_type_char_to_type (int t) +{ + if (t == DBUS_STRUCT_BEGIN_CHAR) + return DBUS_TYPE_STRUCT; + else if (t == DBUS_DICT_ENTRY_BEGIN_CHAR) + return DBUS_TYPE_DICT_ENTRY; + else + { + _dbus_assert (t != DBUS_STRUCT_END_CHAR); + _dbus_assert (t != DBUS_DICT_ENTRY_END_CHAR); + return t; + } +} + +/** + * Get the first type in the signature. The difference between this + * and just getting the first byte of the signature is that you won't + * get DBUS_STRUCT_BEGIN_CHAR, you'll get DBUS_TYPE_STRUCT + * instead. + * + * @param str string containing signature + * @param pos where the signature starts + * @returns the first type in the signature + */ +int +_dbus_first_type_in_signature (const DBusString *str, + int pos) +{ + return map_type_char_to_type (_dbus_string_get_byte (str, pos)); +} + +/** + * Similar to #_dbus_first_type_in_signature, but operates + * on a C string buffer. + * + * @param str a C string buffer + * @param pos where the signature starts + * @returns the first type in the signature + */ +int +_dbus_first_type_in_signature_c_str (const char *str, + int pos) +{ + return map_type_char_to_type (str[pos]); +} + +/** @} */ + +#ifdef DBUS_BUILD_TESTS +#include "dbus-test.h" +#include + +/** + * Reads a block of fixed-length basic values, as an optimization + * vs. reading each one individually into a new buffer. + * + * This function returns the data in-place; it does not make a copy, + * and it does not swap the bytes. + * + * If you ask for #DBUS_TYPE_DOUBLE you will get a "const double*" back + * and the "value" argument should be a "const double**" and so on. + * + * @param str the string to read from + * @param pos position to read from + * @param element_type type of array elements + * @param value place to return the array + * @param n_elements number of array elements to read + * @param byte_order the byte order, used to read the array length + * @param new_pos #NULL or location to store a position after the elements + */ +void +_dbus_marshal_read_fixed_multi (const DBusString *str, + int pos, + int element_type, + void *value, + int n_elements, + int byte_order, + int *new_pos) +{ + int array_len; + int alignment; + + _dbus_assert (dbus_type_is_fixed (element_type)); + _dbus_assert (dbus_type_is_basic (element_type)); + +#if 0 + _dbus_verbose ("reading %d elements of %s\n", + n_elements, _dbus_type_to_string (element_type)); +#endif + + alignment = _dbus_type_get_alignment (element_type); + + pos = _DBUS_ALIGN_VALUE (pos, alignment); + + array_len = n_elements * alignment; + + *(const DBusBasicValue**) value = (void*) _dbus_string_get_const_data_len (str, pos, array_len); + if (new_pos) + *new_pos = pos + array_len; +} + +static void +swap_test_array (void *array, + int len_bytes, + int byte_order, + int alignment) +{ + DBusString t; + + if (alignment == 1) + return; + + _dbus_string_init_const_len (&t, array, len_bytes); + swap_array (&t, 0, len_bytes / alignment, byte_order, alignment); +} + +#define MARSHAL_BASIC(typename, byte_order, literal) \ + do { \ + v_##typename = literal; \ + if (!_dbus_marshal_write_basic (&str, pos, DBUS_TYPE_##typename, \ + &v_##typename, \ + byte_order, NULL)) \ + _dbus_assert_not_reached ("no memory"); \ + } while (0) + +#define DEMARSHAL_BASIC(typename, byte_order) \ + do { \ + _dbus_marshal_read_basic (&str, pos, DBUS_TYPE_##typename, &v_##typename, \ + byte_order, &pos); \ + } while (0) + +#define DEMARSHAL_BASIC_AND_CHECK(typename, byte_order, literal) \ + do { \ + DEMARSHAL_BASIC (typename, byte_order); \ + if (literal != v_##typename) \ + { \ + _dbus_verbose_bytes_of_string (&str, dump_pos, \ + _dbus_string_get_length (&str) - dump_pos); \ + _dbus_assert_not_reached ("demarshaled wrong value"); \ + } \ + } while (0) + +#define MARSHAL_TEST(typename, byte_order, literal) \ + do { \ + MARSHAL_BASIC (typename, byte_order, literal); \ + dump_pos = pos; \ + DEMARSHAL_BASIC_AND_CHECK (typename, byte_order, literal); \ + } while (0) + +#define MARSHAL_TEST_STRCMP(typename, byte_order, literal) \ + do { \ + MARSHAL_BASIC (typename, byte_order, literal); \ + dump_pos = pos; \ + DEMARSHAL_BASIC (typename, byte_order); \ + if (strcmp (literal, v_##typename) != 0) \ + { \ + _dbus_verbose_bytes_of_string (&str, dump_pos, \ + _dbus_string_get_length (&str) - dump_pos); \ + _dbus_warn ("literal '%s'\nvalue '%s'\n", literal, v_##typename); \ + _dbus_assert_not_reached ("demarshaled wrong value"); \ + } \ + } while (0) + +#define MARSHAL_FIXED_ARRAY(typename, byte_order, literal) \ + do { \ + int next; \ + v_UINT32 = sizeof(literal); \ + if (!_dbus_marshal_write_basic (&str, pos, DBUS_TYPE_UINT32, &v_UINT32, \ + byte_order, &next)) \ + _dbus_assert_not_reached ("no memory"); \ + v_ARRAY_##typename = literal; \ + if (!_dbus_marshal_write_fixed_multi (&str, next, DBUS_TYPE_##typename, \ + &v_ARRAY_##typename, _DBUS_N_ELEMENTS(literal), \ + byte_order, NULL)) \ + _dbus_assert_not_reached ("no memory"); \ + } while (0) + +#define DEMARSHAL_FIXED_ARRAY(typename, byte_order) \ + do { \ + int next; \ + alignment = _dbus_type_get_alignment (DBUS_TYPE_##typename); \ + v_UINT32 = _dbus_marshal_read_uint32 (&str, dump_pos, byte_order, &next); \ + _dbus_marshal_read_fixed_multi (&str, next, DBUS_TYPE_##typename, &v_ARRAY_##typename, \ + v_UINT32/alignment, \ + byte_order, NULL); \ + swap_test_array (v_ARRAY_##typename, v_UINT32, \ + byte_order, alignment); \ + } while (0) + +#define DEMARSHAL_FIXED_ARRAY_AND_CHECK(typename, byte_order, literal) \ + do { \ + DEMARSHAL_FIXED_ARRAY (typename, byte_order); \ + if (memcmp (literal, v_ARRAY_##typename, sizeof (literal) != 0)) \ + { \ + _dbus_verbose ("MARSHALED DATA\n"); \ + _dbus_verbose_bytes_of_string (&str, dump_pos, \ + _dbus_string_get_length (&str) - dump_pos); \ + _dbus_verbose ("LITERAL DATA\n"); \ + _dbus_verbose_bytes ((char*)literal, sizeof (literal), 0); \ + _dbus_verbose ("READ DATA\n"); \ + _dbus_verbose_bytes ((char*)v_ARRAY_##typename, sizeof (literal), 0); \ + _dbus_assert_not_reached ("demarshaled wrong fixed array value"); \ + } \ + } while (0) + +#define MARSHAL_TEST_FIXED_ARRAY(typename, byte_order, literal) \ + do { \ + MARSHAL_FIXED_ARRAY (typename, byte_order, literal); \ + dump_pos = pos; \ + DEMARSHAL_FIXED_ARRAY_AND_CHECK (typename, byte_order, literal); \ + } while (0) + +dbus_bool_t +_dbus_marshal_test (void) +{ + int alignment; + DBusString str; + int pos, dump_pos; + unsigned char array1[5] = { 3, 4, 0, 1, 9 }; + dbus_int16_t array2[3] = { 124, 457, 780 }; + dbus_int32_t array4[3] = { 123, 456, 789 }; +#ifdef DBUS_HAVE_INT64 + dbus_int64_t array8[3] = { DBUS_INT64_CONSTANT (0x123ffffffff), + DBUS_INT64_CONSTANT (0x456ffffffff), + DBUS_INT64_CONSTANT (0x789ffffffff) }; + dbus_int64_t *v_ARRAY_INT64; +#endif + unsigned char *v_ARRAY_BYTE; + dbus_int16_t *v_ARRAY_INT16; + dbus_uint16_t *v_ARRAY_UINT16; + dbus_int32_t *v_ARRAY_INT32; + dbus_uint32_t *v_ARRAY_UINT32; + DBusString t; + double v_DOUBLE; + double t_DOUBLE; + dbus_int16_t v_INT16; + dbus_uint16_t v_UINT16; + dbus_int32_t v_INT32; + dbus_uint32_t v_UINT32; + dbus_int64_t v_INT64; + dbus_uint64_t v_UINT64; + unsigned char v_BYTE; + dbus_bool_t v_BOOLEAN; + const char *v_STRING; + const char *v_SIGNATURE; + const char *v_OBJECT_PATH; + int byte_order; + + if (!_dbus_string_init (&str)) + _dbus_assert_not_reached ("failed to init string"); + + pos = 0; + + /* Marshal doubles */ + MARSHAL_BASIC (DOUBLE, DBUS_BIG_ENDIAN, 3.14); + DEMARSHAL_BASIC (DOUBLE, DBUS_BIG_ENDIAN); + t_DOUBLE = 3.14; + if (!_DBUS_DOUBLES_BITWISE_EQUAL (t_DOUBLE, v_DOUBLE)) + _dbus_assert_not_reached ("got wrong double value"); + + MARSHAL_BASIC (DOUBLE, DBUS_LITTLE_ENDIAN, 3.14); + DEMARSHAL_BASIC (DOUBLE, DBUS_LITTLE_ENDIAN); + t_DOUBLE = 3.14; + if (!_DBUS_DOUBLES_BITWISE_EQUAL (t_DOUBLE, v_DOUBLE)) + _dbus_assert_not_reached ("got wrong double value"); + + /* Marshal signed 16 integers */ + MARSHAL_TEST (INT16, DBUS_BIG_ENDIAN, -12345); + MARSHAL_TEST (INT16, DBUS_LITTLE_ENDIAN, -12345); + + /* Marshal unsigned 16 integers */ + MARSHAL_TEST (UINT16, DBUS_BIG_ENDIAN, 0x1234); + MARSHAL_TEST (UINT16, DBUS_LITTLE_ENDIAN, 0x1234); + + /* Marshal signed integers */ + MARSHAL_TEST (INT32, DBUS_BIG_ENDIAN, -12345678); + MARSHAL_TEST (INT32, DBUS_LITTLE_ENDIAN, -12345678); + + /* Marshal unsigned integers */ + MARSHAL_TEST (UINT32, DBUS_BIG_ENDIAN, 0x12345678); + MARSHAL_TEST (UINT32, DBUS_LITTLE_ENDIAN, 0x12345678); + +#ifdef DBUS_HAVE_INT64 + /* Marshal signed integers */ + MARSHAL_TEST (INT64, DBUS_BIG_ENDIAN, DBUS_INT64_CONSTANT (-0x123456789abc7)); + MARSHAL_TEST (INT64, DBUS_LITTLE_ENDIAN, DBUS_INT64_CONSTANT (-0x123456789abc7)); + + /* Marshal unsigned integers */ + MARSHAL_TEST (UINT64, DBUS_BIG_ENDIAN, DBUS_UINT64_CONSTANT (0x123456789abc7)); + MARSHAL_TEST (UINT64, DBUS_LITTLE_ENDIAN, DBUS_UINT64_CONSTANT (0x123456789abc7)); +#endif /* DBUS_HAVE_INT64 */ + + /* Marshal byte */ + MARSHAL_TEST (BYTE, DBUS_BIG_ENDIAN, 5); + MARSHAL_TEST (BYTE, DBUS_LITTLE_ENDIAN, 5); + + /* Marshal all possible bools! */ + MARSHAL_TEST (BOOLEAN, DBUS_BIG_ENDIAN, FALSE); + MARSHAL_TEST (BOOLEAN, DBUS_LITTLE_ENDIAN, FALSE); + MARSHAL_TEST (BOOLEAN, DBUS_BIG_ENDIAN, TRUE); + MARSHAL_TEST (BOOLEAN, DBUS_LITTLE_ENDIAN, TRUE); + + /* Marshal strings */ + MARSHAL_TEST_STRCMP (STRING, DBUS_BIG_ENDIAN, ""); + MARSHAL_TEST_STRCMP (STRING, DBUS_LITTLE_ENDIAN, ""); + MARSHAL_TEST_STRCMP (STRING, DBUS_BIG_ENDIAN, "This is the dbus test string"); + MARSHAL_TEST_STRCMP (STRING, DBUS_LITTLE_ENDIAN, "This is the dbus test string"); + + /* object paths */ + MARSHAL_TEST_STRCMP (OBJECT_PATH, DBUS_BIG_ENDIAN, "/a/b/c"); + MARSHAL_TEST_STRCMP (OBJECT_PATH, DBUS_LITTLE_ENDIAN, "/a/b/c"); + + /* signatures */ + MARSHAL_TEST_STRCMP (SIGNATURE, DBUS_BIG_ENDIAN, ""); + MARSHAL_TEST_STRCMP (SIGNATURE, DBUS_LITTLE_ENDIAN, ""); + MARSHAL_TEST_STRCMP (SIGNATURE, DBUS_BIG_ENDIAN, "a(ii)"); + MARSHAL_TEST_STRCMP (SIGNATURE, DBUS_LITTLE_ENDIAN, "a(ii)"); + + /* Arrays */ + MARSHAL_TEST_FIXED_ARRAY (INT16, DBUS_BIG_ENDIAN, array2); + MARSHAL_TEST_FIXED_ARRAY (INT16, DBUS_LITTLE_ENDIAN, array2); + MARSHAL_TEST_FIXED_ARRAY (UINT16, DBUS_BIG_ENDIAN, array2); + MARSHAL_TEST_FIXED_ARRAY (UINT16, DBUS_LITTLE_ENDIAN, array2); + + MARSHAL_TEST_FIXED_ARRAY (INT32, DBUS_BIG_ENDIAN, array4); + MARSHAL_TEST_FIXED_ARRAY (INT32, DBUS_LITTLE_ENDIAN, array4); + MARSHAL_TEST_FIXED_ARRAY (UINT32, DBUS_BIG_ENDIAN, array4); + MARSHAL_TEST_FIXED_ARRAY (UINT32, DBUS_LITTLE_ENDIAN, array4); + + MARSHAL_TEST_FIXED_ARRAY (BYTE, DBUS_BIG_ENDIAN, array1); + MARSHAL_TEST_FIXED_ARRAY (BYTE, DBUS_LITTLE_ENDIAN, array1); + +#ifdef DBUS_HAVE_INT64 + MARSHAL_TEST_FIXED_ARRAY (INT64, DBUS_BIG_ENDIAN, array8); + MARSHAL_TEST_FIXED_ARRAY (INT64, DBUS_LITTLE_ENDIAN, array8); +#endif + +#if 0 + + /* + * FIXME restore the set/pack tests + */ + +#ifdef DBUS_HAVE_INT64 + /* set/pack 64-bit integers */ + _dbus_string_set_length (&str, 8); + + /* signed little */ + _dbus_marshal_set_int64 (&str, DBUS_LITTLE_ENDIAN, + 0, DBUS_INT64_CONSTANT (-0x123456789abc7)); + + _dbus_assert (DBUS_INT64_CONSTANT (-0x123456789abc7) == + _dbus_unpack_int64 (DBUS_LITTLE_ENDIAN, + _dbus_string_get_const_data (&str))); + + /* signed big */ + _dbus_marshal_set_int64 (&str, DBUS_BIG_ENDIAN, + 0, DBUS_INT64_CONSTANT (-0x123456789abc7)); + + _dbus_assert (DBUS_INT64_CONSTANT (-0x123456789abc7) == + _dbus_unpack_int64 (DBUS_BIG_ENDIAN, + _dbus_string_get_const_data (&str))); + + /* signed little pack */ + _dbus_pack_int64 (DBUS_INT64_CONSTANT (-0x123456789abc7), + DBUS_LITTLE_ENDIAN, + _dbus_string_get_data (&str)); + + _dbus_assert (DBUS_INT64_CONSTANT (-0x123456789abc7) == + _dbus_unpack_int64 (DBUS_LITTLE_ENDIAN, + _dbus_string_get_const_data (&str))); + + /* signed big pack */ + _dbus_pack_int64 (DBUS_INT64_CONSTANT (-0x123456789abc7), + DBUS_BIG_ENDIAN, + _dbus_string_get_data (&str)); + + _dbus_assert (DBUS_INT64_CONSTANT (-0x123456789abc7) == + _dbus_unpack_int64 (DBUS_BIG_ENDIAN, + _dbus_string_get_const_data (&str))); + + /* unsigned little */ + _dbus_marshal_set_uint64 (&str, DBUS_LITTLE_ENDIAN, + 0, DBUS_UINT64_CONSTANT (0x123456789abc7)); + + _dbus_assert (DBUS_UINT64_CONSTANT (0x123456789abc7) == + _dbus_unpack_uint64 (DBUS_LITTLE_ENDIAN, + _dbus_string_get_const_data (&str))); + + /* unsigned big */ + _dbus_marshal_set_uint64 (&str, DBUS_BIG_ENDIAN, + 0, DBUS_UINT64_CONSTANT (0x123456789abc7)); + + _dbus_assert (DBUS_UINT64_CONSTANT (0x123456789abc7) == + _dbus_unpack_uint64 (DBUS_BIG_ENDIAN, + _dbus_string_get_const_data (&str))); + + /* unsigned little pack */ + _dbus_pack_uint64 (DBUS_UINT64_CONSTANT (0x123456789abc7), + DBUS_LITTLE_ENDIAN, + _dbus_string_get_data (&str)); + + _dbus_assert (DBUS_UINT64_CONSTANT (0x123456789abc7) == + _dbus_unpack_uint64 (DBUS_LITTLE_ENDIAN, + _dbus_string_get_const_data (&str))); + + /* unsigned big pack */ + _dbus_pack_uint64 (DBUS_UINT64_CONSTANT (0x123456789abc7), + DBUS_BIG_ENDIAN, + _dbus_string_get_data (&str)); + + _dbus_assert (DBUS_UINT64_CONSTANT (0x123456789abc7) == + _dbus_unpack_uint64 (DBUS_BIG_ENDIAN, + _dbus_string_get_const_data (&str))); +#endif /* DBUS_HAVE_INT64 */ + + /* set/pack 32-bit integers */ + _dbus_string_set_length (&str, 4); + + /* signed little */ + _dbus_marshal_set_int32 (&str, DBUS_LITTLE_ENDIAN, + 0, -0x123456); + + _dbus_assert (-0x123456 == + _dbus_unpack_int32 (DBUS_LITTLE_ENDIAN, + _dbus_string_get_const_data (&str))); + + /* signed big */ + _dbus_marshal_set_int32 (&str, DBUS_BIG_ENDIAN, + 0, -0x123456); + + _dbus_assert (-0x123456 == + _dbus_unpack_int32 (DBUS_BIG_ENDIAN, + _dbus_string_get_const_data (&str))); + + /* signed little pack */ + _dbus_pack_int32 (-0x123456, + DBUS_LITTLE_ENDIAN, + _dbus_string_get_data (&str)); + + _dbus_assert (-0x123456 == + _dbus_unpack_int32 (DBUS_LITTLE_ENDIAN, + _dbus_string_get_const_data (&str))); + + /* signed big pack */ + _dbus_pack_int32 (-0x123456, + DBUS_BIG_ENDIAN, + _dbus_string_get_data (&str)); + + _dbus_assert (-0x123456 == + _dbus_unpack_int32 (DBUS_BIG_ENDIAN, + _dbus_string_get_const_data (&str))); + + /* unsigned little */ + _dbus_marshal_set_uint32 (&str, + 0, 0x123456, + DBUS_LITTLE_ENDIAN); + + _dbus_assert (0x123456 == + _dbus_unpack_uint32 (DBUS_LITTLE_ENDIAN, + _dbus_string_get_const_data (&str))); + + /* unsigned big */ + _dbus_marshal_set_uint32 (&str, + 0, 0x123456, + DBUS_BIG_ENDIAN); + + _dbus_assert (0x123456 == + _dbus_unpack_uint32 (DBUS_BIG_ENDIAN, + _dbus_string_get_const_data (&str))); + + /* unsigned little pack */ + _dbus_pack_uint32 (0x123456, + DBUS_LITTLE_ENDIAN, + _dbus_string_get_data (&str)); + + _dbus_assert (0x123456 == + _dbus_unpack_uint32 (DBUS_LITTLE_ENDIAN, + _dbus_string_get_const_data (&str))); + + /* unsigned big pack */ + _dbus_pack_uint32 (0x123456, + DBUS_BIG_ENDIAN, + _dbus_string_get_data (&str)); + + _dbus_assert (0x123456 == + _dbus_unpack_uint32 (DBUS_BIG_ENDIAN, + _dbus_string_get_const_data (&str))); + +#endif /* set/pack tests for integers */ + + /* Strings in-place set */ + byte_order = DBUS_LITTLE_ENDIAN; + while (TRUE) + { + /* Init a string */ + _dbus_string_set_length (&str, 0); + + /* reset pos for the macros */ + pos = 0; + + MARSHAL_TEST_STRCMP (STRING, byte_order, "Hello world"); + + /* Set it to something longer */ + _dbus_string_init_const (&t, "Hello world foo"); + + v_STRING = _dbus_string_get_const_data (&t); + _dbus_marshal_set_basic (&str, 0, DBUS_TYPE_STRING, + &v_STRING, byte_order, NULL, NULL); + + _dbus_marshal_read_basic (&str, 0, DBUS_TYPE_STRING, + &v_STRING, byte_order, + NULL); + _dbus_assert (strcmp (v_STRING, "Hello world foo") == 0); + + /* Set it to something shorter */ + _dbus_string_init_const (&t, "Hello"); + + v_STRING = _dbus_string_get_const_data (&t); + _dbus_marshal_set_basic (&str, 0, DBUS_TYPE_STRING, + &v_STRING, byte_order, NULL, NULL); + _dbus_marshal_read_basic (&str, 0, DBUS_TYPE_STRING, + &v_STRING, byte_order, + NULL); + _dbus_assert (strcmp (v_STRING, "Hello") == 0); + + /* Do the other byte order */ + if (byte_order == DBUS_LITTLE_ENDIAN) + byte_order = DBUS_BIG_ENDIAN; + else + break; + } + + /* Clean up */ + _dbus_string_free (&str); + + return TRUE; +} + +#endif /* DBUS_BUILD_TESTS */ diff --git a/src/dbus/dbus-marshal-basic.h b/src/dbus/dbus-marshal-basic.h new file mode 100644 index 0000000..28c751f --- /dev/null +++ b/src/dbus/dbus-marshal-basic.h @@ -0,0 +1,261 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-marshal-basic.h Marshalling routines for basic (primitive) types + * + * Copyright (C) 2002 CodeFactory AB + * Copyright (C) 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef DBUS_MARSHAL_BASIC_H +#define DBUS_MARSHAL_BASIC_H + +#include +#include +#include +#include +#include + +#ifndef PACKAGE +#error "config.h not included here" +#endif + +#ifdef WORDS_BIGENDIAN +#define DBUS_COMPILER_BYTE_ORDER DBUS_BIG_ENDIAN +#else +#define DBUS_COMPILER_BYTE_ORDER DBUS_LITTLE_ENDIAN +#endif + +#define DBUS_UINT16_SWAP_LE_BE_CONSTANT(val) ((dbus_uint16_t) ( \ + (dbus_uint16_t) ((dbus_uint16_t) (val) >> 8) | \ + (dbus_uint16_t) ((dbus_uint16_t) (val) << 8))) + +#define DBUS_UINT32_SWAP_LE_BE_CONSTANT(val) ((dbus_uint32_t) ( \ + (((dbus_uint32_t) (val) & (dbus_uint32_t) 0x000000ffU) << 24) | \ + (((dbus_uint32_t) (val) & (dbus_uint32_t) 0x0000ff00U) << 8) | \ + (((dbus_uint32_t) (val) & (dbus_uint32_t) 0x00ff0000U) >> 8) | \ + (((dbus_uint32_t) (val) & (dbus_uint32_t) 0xff000000U) >> 24))) + +#ifdef DBUS_HAVE_INT64 + +#define DBUS_UINT64_SWAP_LE_BE_CONSTANT(val) ((dbus_uint64_t) ( \ + (((dbus_uint64_t) (val) & \ + (dbus_uint64_t) DBUS_UINT64_CONSTANT (0x00000000000000ff)) << 56) | \ + (((dbus_uint64_t) (val) & \ + (dbus_uint64_t) DBUS_UINT64_CONSTANT (0x000000000000ff00)) << 40) | \ + (((dbus_uint64_t) (val) & \ + (dbus_uint64_t) DBUS_UINT64_CONSTANT (0x0000000000ff0000)) << 24) | \ + (((dbus_uint64_t) (val) & \ + (dbus_uint64_t) DBUS_UINT64_CONSTANT (0x00000000ff000000)) << 8) | \ + (((dbus_uint64_t) (val) & \ + (dbus_uint64_t) DBUS_UINT64_CONSTANT (0x000000ff00000000)) >> 8) | \ + (((dbus_uint64_t) (val) & \ + (dbus_uint64_t) DBUS_UINT64_CONSTANT (0x0000ff0000000000)) >> 24) | \ + (((dbus_uint64_t) (val) & \ + (dbus_uint64_t) DBUS_UINT64_CONSTANT (0x00ff000000000000)) >> 40) | \ + (((dbus_uint64_t) (val) & \ + (dbus_uint64_t) DBUS_UINT64_CONSTANT (0xff00000000000000)) >> 56))) +#endif /* DBUS_HAVE_INT64 */ + +#define DBUS_UINT16_SWAP_LE_BE(val) (DBUS_UINT16_SWAP_LE_BE_CONSTANT (val)) +#define DBUS_INT16_SWAP_LE_BE(val) ((dbus_int16_t)DBUS_UINT16_SWAP_LE_BE_CONSTANT (val)) + +#define DBUS_UINT32_SWAP_LE_BE(val) (DBUS_UINT32_SWAP_LE_BE_CONSTANT (val)) +#define DBUS_INT32_SWAP_LE_BE(val) ((dbus_int32_t)DBUS_UINT32_SWAP_LE_BE_CONSTANT (val)) + +#ifdef DBUS_HAVE_INT64 +# define DBUS_UINT64_SWAP_LE_BE(val) (DBUS_UINT64_SWAP_LE_BE_CONSTANT (val)) +# define DBUS_INT64_SWAP_LE_BE(val) ((dbus_int64_t)DBUS_UINT64_SWAP_LE_BE_CONSTANT (val)) +#endif /* DBUS_HAVE_INT64 */ + +#ifdef WORDS_BIGENDIAN + +# define DBUS_INT16_TO_BE(val) ((dbus_int16_t) (val)) +# define DBUS_UINT16_TO_BE(val) ((dbus_uint16_t) (val)) +# define DBUS_INT16_TO_LE(val) (DBUS_INT16_SWAP_LE_BE (val)) +# define DBUS_UINT16_TO_LE(val) (DBUS_UINT16_SWAP_LE_BE (val)) +# define DBUS_INT32_TO_BE(val) ((dbus_int32_t) (val)) +# define DBUS_UINT32_TO_BE(val) ((dbus_uint32_t) (val)) +# define DBUS_INT32_TO_LE(val) (DBUS_INT32_SWAP_LE_BE (val)) +# define DBUS_UINT32_TO_LE(val) (DBUS_UINT32_SWAP_LE_BE (val)) +# ifdef DBUS_HAVE_INT64 +# define DBUS_INT64_TO_BE(val) ((dbus_int64_t) (val)) +# define DBUS_UINT64_TO_BE(val) ((dbus_uint64_t) (val)) +# define DBUS_INT64_TO_LE(val) (DBUS_INT64_SWAP_LE_BE (val)) +# define DBUS_UINT64_TO_LE(val) (DBUS_UINT64_SWAP_LE_BE (val)) +# endif /* DBUS_HAVE_INT64 */ + +#else /* WORDS_BIGENDIAN */ + +# define DBUS_INT16_TO_LE(val) ((dbus_int16_t) (val)) +# define DBUS_UINT16_TO_LE(val) ((dbus_uint16_t) (val)) +# define DBUS_INT16_TO_BE(val) ((dbus_int16_t) DBUS_UINT16_SWAP_LE_BE (val)) +# define DBUS_UINT16_TO_BE(val) (DBUS_UINT16_SWAP_LE_BE (val)) +# define DBUS_INT32_TO_LE(val) ((dbus_int32_t) (val)) +# define DBUS_UINT32_TO_LE(val) ((dbus_uint32_t) (val)) +# define DBUS_INT32_TO_BE(val) ((dbus_int32_t) DBUS_UINT32_SWAP_LE_BE (val)) +# define DBUS_UINT32_TO_BE(val) (DBUS_UINT32_SWAP_LE_BE (val)) +# ifdef DBUS_HAVE_INT64 +# define DBUS_INT64_TO_LE(val) ((dbus_int64_t) (val)) +# define DBUS_UINT64_TO_LE(val) ((dbus_uint64_t) (val)) +# define DBUS_INT64_TO_BE(val) ((dbus_int64_t) DBUS_UINT64_SWAP_LE_BE (val)) +# define DBUS_UINT64_TO_BE(val) (DBUS_UINT64_SWAP_LE_BE (val)) +# endif /* DBUS_HAVE_INT64 */ +#endif + +/* The transformation is symmetric, so the FROM just maps to the TO. */ +#define DBUS_INT16_FROM_LE(val) (DBUS_INT16_TO_LE (val)) +#define DBUS_UINT16_FROM_LE(val) (DBUS_UINT16_TO_LE (val)) +#define DBUS_INT16_FROM_BE(val) (DBUS_INT16_TO_BE (val)) +#define DBUS_UINT16_FROM_BE(val) (DBUS_UINT16_TO_BE (val)) +#define DBUS_INT32_FROM_LE(val) (DBUS_INT32_TO_LE (val)) +#define DBUS_UINT32_FROM_LE(val) (DBUS_UINT32_TO_LE (val)) +#define DBUS_INT32_FROM_BE(val) (DBUS_INT32_TO_BE (val)) +#define DBUS_UINT32_FROM_BE(val) (DBUS_UINT32_TO_BE (val)) +#ifdef DBUS_HAVE_INT64 +# define DBUS_INT64_FROM_LE(val) (DBUS_INT64_TO_LE (val)) +# define DBUS_UINT64_FROM_LE(val) (DBUS_UINT64_TO_LE (val)) +# define DBUS_INT64_FROM_BE(val) (DBUS_INT64_TO_BE (val)) +# define DBUS_UINT64_FROM_BE(val) (DBUS_UINT64_TO_BE (val)) +#endif /* DBUS_HAVE_INT64 */ + +#ifndef DBUS_HAVE_INT64 +/** + * An 8-byte struct you could use to access int64 without having + * int64 support + */ +typedef struct +{ + dbus_uint32_t first32; /**< first 32 bits in the 8 bytes (beware endian issues) */ + dbus_uint32_t second32; /**< second 32 bits in the 8 bytes (beware endian issues) */ +} DBus8ByteStruct; +#endif /* DBUS_HAVE_INT64 */ + +/** + * A simple 8-byte value union that lets you access 8 bytes as if they + * were various types; useful when dealing with basic types via + * void pointers and varargs. + */ +typedef union +{ + dbus_int16_t i16; /**< as int16 */ + dbus_uint16_t u16; /**< as int16 */ + dbus_int32_t i32; /**< as int32 */ + dbus_uint32_t u32; /**< as int32 */ +#ifdef DBUS_HAVE_INT64 + dbus_int64_t i64; /**< as int64 */ + dbus_uint64_t u64; /**< as int64 */ +#else + DBus8ByteStruct u64; /**< as 8-byte-struct */ +#endif + double dbl; /**< as double */ + unsigned char byt; /**< as byte */ + char *str; /**< as char* */ +} DBusBasicValue; + +#ifdef DBUS_DISABLE_ASSERT +#define _dbus_unpack_uint16(byte_order, data) \ + (((byte_order) == DBUS_LITTLE_ENDIAN) ? \ + DBUS_UINT16_FROM_LE (*(dbus_uint16_t*)(data)) : \ + DBUS_UINT16_FROM_BE (*(dbus_uint16_t*)(data))) + +#define _dbus_unpack_uint32(byte_order, data) \ + (((byte_order) == DBUS_LITTLE_ENDIAN) ? \ + DBUS_UINT32_FROM_LE (*(dbus_uint32_t*)(data)) : \ + DBUS_UINT32_FROM_BE (*(dbus_uint32_t*)(data))) +#endif + +#ifndef _dbus_unpack_uint16 +dbus_uint16_t _dbus_unpack_uint16 (int byte_order, + const unsigned char *data); +#endif + +void _dbus_pack_uint32 (dbus_uint32_t value, + int byte_order, + unsigned char *data); +#ifndef _dbus_unpack_uint32 +dbus_uint32_t _dbus_unpack_uint32 (int byte_order, + const unsigned char *data); +#endif + +dbus_bool_t _dbus_marshal_set_basic (DBusString *str, + int pos, + int type, + const void *value, + int byte_order, + int *old_end_pos, + int *new_end_pos); +dbus_bool_t _dbus_marshal_write_basic (DBusString *str, + int insert_at, + int type, + const void *value, + int byte_order, + int *pos_after); +dbus_bool_t _dbus_marshal_write_fixed_multi (DBusString *str, + int insert_at, + int element_type, + const void *value, + int n_elements, + int byte_order, + int *pos_after); +void _dbus_marshal_read_basic (const DBusString *str, + int pos, + int type, + void *value, + int byte_order, + int *new_pos); +void _dbus_marshal_read_fixed_multi (const DBusString *str, + int pos, + int element_type, + void *value, + int n_elements, + int byte_order, + int *new_pos); +void _dbus_marshal_skip_basic (const DBusString *str, + int type, + int byte_order, + int *pos); +void _dbus_marshal_skip_array (const DBusString *str, + int element_type, + int byte_order, + int *pos); +void _dbus_marshal_set_uint32 (DBusString *str, + int pos, + dbus_uint32_t value, + int byte_order); +dbus_uint32_t _dbus_marshal_read_uint32 (const DBusString *str, + int pos, + int byte_order, + int *new_pos); +dbus_bool_t _dbus_type_is_valid (int typecode); +int _dbus_type_get_alignment (int typecode); +dbus_bool_t _dbus_type_is_fixed (int typecode); +int _dbus_type_get_alignment (int typecode); +const char* _dbus_type_to_string (int typecode); + +int _dbus_first_type_in_signature (const DBusString *str, + int pos); + +int _dbus_first_type_in_signature_c_str (const char *str, + int pos); + +void _dbus_swap_array (unsigned char *data, + int n_elements, + int alignment); + +#endif /* DBUS_MARSHAL_BASIC_H */ diff --git a/src/dbus/dbus-marshal-byteswap-util.c b/src/dbus/dbus-marshal-byteswap-util.c new file mode 100644 index 0000000..135852e --- /dev/null +++ b/src/dbus/dbus-marshal-byteswap-util.c @@ -0,0 +1,105 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-marshal-byteswap-util.c Would be in dbus-marshal-byteswap.c but tests/bus only + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include + +#ifdef DBUS_BUILD_TESTS +#include "dbus-marshal-byteswap.h" +#include "dbus-test.h" +#include + +static void +do_byteswap_test (int byte_order) +{ + int sequence; + DBusString signature; + DBusString body; + int opposite_order; + + if (!_dbus_string_init (&signature) || !_dbus_string_init (&body)) + _dbus_assert_not_reached ("oom"); + + opposite_order = byte_order == DBUS_LITTLE_ENDIAN ? DBUS_BIG_ENDIAN : DBUS_LITTLE_ENDIAN; + + sequence = 0; + while (dbus_internal_do_not_use_generate_bodies (sequence, + byte_order, + &signature, &body)) + { + DBusString copy; + DBusTypeReader body_reader; + DBusTypeReader copy_reader; + + if (!_dbus_string_init (©)) + _dbus_assert_not_reached ("oom"); + + if (!_dbus_string_copy (&body, 0, ©, 0)) + _dbus_assert_not_reached ("oom"); + + _dbus_marshal_byteswap (&signature, 0, + byte_order, + opposite_order, + ©, 0); + + _dbus_type_reader_init (&body_reader, byte_order, &signature, 0, + &body, 0); + _dbus_type_reader_init (©_reader, opposite_order, &signature, 0, + ©, 0); + + if (!_dbus_type_reader_equal_values (&body_reader, ©_reader)) + { + _dbus_verbose_bytes_of_string (&signature, 0, + _dbus_string_get_length (&signature)); + _dbus_verbose_bytes_of_string (&body, 0, + _dbus_string_get_length (&body)); + _dbus_verbose_bytes_of_string (©, 0, + _dbus_string_get_length (©)); + + _dbus_warn ("Byte-swapped data did not have same values as original data\n"); + _dbus_assert_not_reached ("test failed"); + } + + _dbus_string_free (©); + + _dbus_string_set_length (&signature, 0); + _dbus_string_set_length (&body, 0); + ++sequence; + } + + _dbus_string_free (&signature); + _dbus_string_free (&body); + + printf (" %d blocks swapped from order '%c' to '%c'\n", + sequence, byte_order, opposite_order); +} + +dbus_bool_t +_dbus_marshal_byteswap_test (void) +{ + do_byteswap_test (DBUS_LITTLE_ENDIAN); + do_byteswap_test (DBUS_BIG_ENDIAN); + + return TRUE; +} + +#endif /* DBUS_BUILD_TESTS */ diff --git a/src/dbus/dbus-marshal-byteswap.c b/src/dbus/dbus-marshal-byteswap.c new file mode 100644 index 0000000..6c9fff5 --- /dev/null +++ b/src/dbus/dbus-marshal-byteswap.c @@ -0,0 +1,246 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-marshal-byteswap.c Swap a block of marshaled data + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-marshal-byteswap.h" +#include "dbus-marshal-basic.h" +#include "dbus-signature.h" + +/** + * @addtogroup DBusMarshal + * @{ + */ + +static void +byteswap_body_helper (DBusTypeReader *reader, + dbus_bool_t walk_reader_to_end, + int old_byte_order, + int new_byte_order, + unsigned char *p, + unsigned char **new_p) +{ + int current_type; + + while ((current_type = _dbus_type_reader_get_current_type (reader)) != DBUS_TYPE_INVALID) + { + switch (current_type) + { + case DBUS_TYPE_BYTE: + ++p; + break; + + case DBUS_TYPE_INT16: + case DBUS_TYPE_UINT16: + { + p = _DBUS_ALIGN_ADDRESS (p, 2); + *((dbus_uint16_t*)p) = DBUS_UINT16_SWAP_LE_BE (*((dbus_uint16_t*)p)); + p += 2; + } + break; + + case DBUS_TYPE_BOOLEAN: + case DBUS_TYPE_INT32: + case DBUS_TYPE_UINT32: + { + p = _DBUS_ALIGN_ADDRESS (p, 4); + *((dbus_uint32_t*)p) = DBUS_UINT32_SWAP_LE_BE (*((dbus_uint32_t*)p)); + p += 4; + } + break; + + case DBUS_TYPE_INT64: + case DBUS_TYPE_UINT64: + case DBUS_TYPE_DOUBLE: + { + p = _DBUS_ALIGN_ADDRESS (p, 8); +#ifdef DBUS_HAVE_INT64 + *((dbus_uint64_t*)p) = DBUS_UINT64_SWAP_LE_BE (*((dbus_uint64_t*)p)); +#else + _dbus_swap_array (p, 1, 8); +#endif + p += 8; + } + break; + + case DBUS_TYPE_ARRAY: + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + { + dbus_uint32_t array_len; + + p = _DBUS_ALIGN_ADDRESS (p, 4); + + array_len = _dbus_unpack_uint32 (old_byte_order, p); + + *((dbus_uint32_t*)p) = DBUS_UINT32_SWAP_LE_BE (*((dbus_uint32_t*)p)); + p += 4; + + if (current_type == DBUS_TYPE_ARRAY) + { + int elem_type; + int alignment; + + elem_type = _dbus_type_reader_get_element_type (reader); + alignment = _dbus_type_get_alignment (elem_type); + + _dbus_assert ((array_len / alignment) < DBUS_MAXIMUM_ARRAY_LENGTH); + + p = _DBUS_ALIGN_ADDRESS (p, alignment); + + if (dbus_type_is_fixed (elem_type)) + { + if (alignment > 1) + _dbus_swap_array (p, array_len / alignment, alignment); + p += array_len; + } + else + { + DBusTypeReader sub; + const unsigned char *array_end; + + array_end = p + array_len; + + _dbus_type_reader_recurse (reader, &sub); + + while (p < array_end) + { + byteswap_body_helper (&sub, + FALSE, + old_byte_order, + new_byte_order, + p, &p); + } + } + } + else + { + _dbus_assert (current_type == DBUS_TYPE_STRING || + current_type == DBUS_TYPE_OBJECT_PATH); + + p += (array_len + 1); /* + 1 for nul */ + } + } + break; + + case DBUS_TYPE_SIGNATURE: + { + dbus_uint32_t sig_len; + + sig_len = *p; + + p += (sig_len + 2); /* +2 for len and nul */ + } + break; + + case DBUS_TYPE_VARIANT: + { + /* 1 byte sig len, sig typecodes, align to + * contained-type-boundary, values. + */ + dbus_uint32_t sig_len; + DBusString sig; + DBusTypeReader sub; + int contained_alignment; + + sig_len = *p; + ++p; + + _dbus_string_init_const_len (&sig, p, sig_len); + + p += (sig_len + 1); /* 1 for nul */ + + contained_alignment = _dbus_type_get_alignment (_dbus_first_type_in_signature (&sig, 0)); + + p = _DBUS_ALIGN_ADDRESS (p, contained_alignment); + + _dbus_type_reader_init_types_only (&sub, &sig, 0); + + byteswap_body_helper (&sub, FALSE, old_byte_order, new_byte_order, p, &p); + } + break; + + case DBUS_TYPE_STRUCT: + case DBUS_TYPE_DICT_ENTRY: + { + DBusTypeReader sub; + + p = _DBUS_ALIGN_ADDRESS (p, 8); + + _dbus_type_reader_recurse (reader, &sub); + + byteswap_body_helper (&sub, TRUE, old_byte_order, new_byte_order, p, &p); + } + break; + + default: + _dbus_assert_not_reached ("invalid typecode in supposedly-validated signature"); + break; + } + + if (walk_reader_to_end) + _dbus_type_reader_next (reader); + else + break; + } + + if (new_p) + *new_p = p; +} + +/** + * Byteswaps the marshaled data in the given value_str. + * + * @param signature the types in the value_str + * @param signature_start where in signature is the signature + * @param old_byte_order the old byte order + * @param new_byte_order the new byte order + * @param value_str the string containing the body + * @param value_pos where the values start + */ +void +_dbus_marshal_byteswap (const DBusString *signature, + int signature_start, + int old_byte_order, + int new_byte_order, + DBusString *value_str, + int value_pos) +{ + DBusTypeReader reader; + + _dbus_assert (value_pos >= 0); + _dbus_assert (value_pos <= _dbus_string_get_length (value_str)); + + if (old_byte_order == new_byte_order) + return; + + _dbus_type_reader_init_types_only (&reader, + signature, signature_start); + + byteswap_body_helper (&reader, TRUE, + old_byte_order, new_byte_order, + _dbus_string_get_data_len (value_str, value_pos, 0), + NULL); +} + +/** @} */ + +/* Tests in dbus-marshal-byteswap-util.c */ diff --git a/src/dbus/dbus-marshal-byteswap.h b/src/dbus/dbus-marshal-byteswap.h new file mode 100644 index 0000000..880c837 --- /dev/null +++ b/src/dbus/dbus-marshal-byteswap.h @@ -0,0 +1,42 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-marshal-byteswap.h Swap a block of marshaled data + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef DBUS_MARSHAL_BYTESWAP_H +#define DBUS_MARSHAL_BYTESWAP_H + +#include +#include +#include + +#ifndef PACKAGE +#error "config.h not included here" +#endif + +void _dbus_marshal_byteswap (const DBusString *signature, + int signature_start, + int old_byte_order, + int new_byte_order, + DBusString *value_str, + int value_pos); + +#endif /* DBUS_MARSHAL_BYTESWAP_H */ diff --git a/src/dbus/dbus-marshal-header.c b/src/dbus/dbus-marshal-header.c new file mode 100644 index 0000000..8aba6a9 --- /dev/null +++ b/src/dbus/dbus-marshal-header.c @@ -0,0 +1,1489 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-marshal-header.c Managing marshaling/demarshaling of message headers + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus/dbus-shared.h" +#include "dbus-marshal-header.h" +#include "dbus-marshal-recursive.h" +#include "dbus-marshal-byteswap.h" + +/** + * @addtogroup DBusMarshal + * + * @{ + */ + + +/* Not thread locked, but strictly const/read-only so should be OK + */ +/** Static #DBusString containing the signature of a message header */ +_DBUS_STRING_DEFINE_STATIC(_dbus_header_signature_str, DBUS_HEADER_SIGNATURE); +/** Static #DBusString containing the local interface */ +_DBUS_STRING_DEFINE_STATIC(_dbus_local_interface_str, DBUS_INTERFACE_LOCAL); +/** Static #DBusString containing the local path */ +_DBUS_STRING_DEFINE_STATIC(_dbus_local_path_str, DBUS_PATH_LOCAL); + +/** Offset from start of _dbus_header_signature_str to the signature of the fields array */ +#define FIELDS_ARRAY_SIGNATURE_OFFSET 6 +/** Offset from start of _dbus_header_signature_str to the signature of an element of the fields array */ +#define FIELDS_ARRAY_ELEMENT_SIGNATURE_OFFSET 7 + + +/** Offset to byte order from start of header */ +#define BYTE_ORDER_OFFSET 0 +/** Offset to type from start of header */ +#define TYPE_OFFSET 1 +/** Offset to flags from start of header */ +#define FLAGS_OFFSET 2 +/** Offset to version from start of header */ +#define VERSION_OFFSET 3 +/** Offset to body length from start of header */ +#define BODY_LENGTH_OFFSET 4 +/** Offset to client serial from start of header */ +#define SERIAL_OFFSET 8 +/** Offset to fields array length from start of header */ +#define FIELDS_ARRAY_LENGTH_OFFSET 12 +/** Offset to first field in header */ +#define FIRST_FIELD_OFFSET 16 + +typedef struct +{ + unsigned char code; /**< the field code */ + unsigned char type; /**< the value type */ +} HeaderFieldType; + +static const HeaderFieldType +_dbus_header_field_types[DBUS_HEADER_FIELD_LAST+1] = { + { DBUS_HEADER_FIELD_INVALID, DBUS_TYPE_INVALID }, + { DBUS_HEADER_FIELD_PATH, DBUS_TYPE_OBJECT_PATH }, + { DBUS_HEADER_FIELD_INTERFACE, DBUS_TYPE_STRING }, + { DBUS_HEADER_FIELD_MEMBER, DBUS_TYPE_STRING }, + { DBUS_HEADER_FIELD_ERROR_NAME, DBUS_TYPE_STRING }, + { DBUS_HEADER_FIELD_REPLY_SERIAL, DBUS_TYPE_UINT32 }, + { DBUS_HEADER_FIELD_DESTINATION, DBUS_TYPE_STRING }, + { DBUS_HEADER_FIELD_SENDER, DBUS_TYPE_STRING }, + { DBUS_HEADER_FIELD_SIGNATURE, DBUS_TYPE_SIGNATURE } +}; + +/** Macro to look up the correct type for a field */ +#define EXPECTED_TYPE_OF_FIELD(field) (_dbus_header_field_types[field].type) + +/** The most padding we could ever need for a header */ +#define MAX_POSSIBLE_HEADER_PADDING 7 +static dbus_bool_t +reserve_header_padding (DBusHeader *header) +{ + _dbus_assert (header->padding <= MAX_POSSIBLE_HEADER_PADDING); + + if (!_dbus_string_lengthen (&header->data, + MAX_POSSIBLE_HEADER_PADDING - header->padding)) + return FALSE; + header->padding = MAX_POSSIBLE_HEADER_PADDING; + return TRUE; +} + +static void +correct_header_padding (DBusHeader *header) +{ + int unpadded_len; + + _dbus_assert (header->padding == 7); + + _dbus_string_shorten (&header->data, header->padding); + unpadded_len = _dbus_string_get_length (&header->data); + + if (!_dbus_string_align_length (&header->data, 8)) + _dbus_assert_not_reached ("couldn't pad header though enough padding was preallocated"); + + header->padding = _dbus_string_get_length (&header->data) - unpadded_len; +} + +/** Compute the end of the header, ignoring padding */ +#define HEADER_END_BEFORE_PADDING(header) \ + (_dbus_string_get_length (&(header)->data) - (header)->padding) + +/** + * Invalidates all fields in the cache. This may be used when the + * cache is totally uninitialized (contains junk) so should not + * look at what's in there now. + * + * @param header the header + */ +static void +_dbus_header_cache_invalidate_all (DBusHeader *header) +{ + int i; + + i = 0; + while (i <= DBUS_HEADER_FIELD_LAST) + { + header->fields[i].value_pos = _DBUS_HEADER_FIELD_VALUE_UNKNOWN; + ++i; + } +} + +/** + * Caches one field + * + * @param header the header + * @param field_code the field + * @param variant_reader the reader for the variant in the field + */ +static void +_dbus_header_cache_one (DBusHeader *header, + int field_code, + DBusTypeReader *variant_reader) +{ + header->fields[field_code].value_pos = + _dbus_type_reader_get_value_pos (variant_reader); + +#if 0 + _dbus_verbose ("cached value_pos %d for field %d\n", + header->fields[field_code].value_pos, field_code) +#endif +} + +/** + * Revalidates the fields cache + * + * @param header the header + */ +static void +_dbus_header_cache_revalidate (DBusHeader *header) +{ + DBusTypeReader array; + DBusTypeReader reader; + int i; + + i = 0; + while (i <= DBUS_HEADER_FIELD_LAST) + { + header->fields[i].value_pos = _DBUS_HEADER_FIELD_VALUE_NONEXISTENT; + ++i; + } + + _dbus_type_reader_init (&reader, + header->byte_order, + &_dbus_header_signature_str, + FIELDS_ARRAY_SIGNATURE_OFFSET, + &header->data, + FIELDS_ARRAY_LENGTH_OFFSET); + + _dbus_type_reader_recurse (&reader, &array); + + while (_dbus_type_reader_get_current_type (&array) != DBUS_TYPE_INVALID) + { + DBusTypeReader sub; + DBusTypeReader variant; + unsigned char field_code; + + _dbus_type_reader_recurse (&array, &sub); + + _dbus_assert (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_BYTE); + _dbus_type_reader_read_basic (&sub, &field_code); + + /* Unknown fields should be ignored */ + if (field_code > DBUS_HEADER_FIELD_LAST) + goto next_field; + + _dbus_type_reader_next (&sub); + + _dbus_assert (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_VARIANT); + _dbus_type_reader_recurse (&sub, &variant); + + _dbus_header_cache_one (header, field_code, &variant); + + next_field: + _dbus_type_reader_next (&array); + } +} + +/** + * Checks for a field, updating the cache if required. + * + * @param header the header + * @param field the field to check + * @returns #FALSE if the field doesn't exist + */ +static dbus_bool_t +_dbus_header_cache_check (DBusHeader *header, + int field) +{ + _dbus_assert (field <= DBUS_HEADER_FIELD_LAST); + + if (header->fields[field].value_pos == _DBUS_HEADER_FIELD_VALUE_UNKNOWN) + _dbus_header_cache_revalidate (header); + + if (header->fields[field].value_pos == _DBUS_HEADER_FIELD_VALUE_NONEXISTENT) + return FALSE; + + return TRUE; +} + +/** + * Checks whether a field is known not to exist. It may exist + * even if it's not known to exist. + * + * @param header the header + * @param field the field to check + * @returns #FALSE if the field definitely doesn't exist + */ +static dbus_bool_t +_dbus_header_cache_known_nonexistent (DBusHeader *header, + int field) +{ + _dbus_assert (field <= DBUS_HEADER_FIELD_LAST); + + return (header->fields[field].value_pos == _DBUS_HEADER_FIELD_VALUE_NONEXISTENT); +} + +/** + * Writes a struct of { byte, variant } with the given basic type. + * + * @param writer the writer (should be ready to write a struct) + * @param type the type of the value + * @param value the value as for _dbus_marshal_set_basic() + * @returns #FALSE if no memory + */ +static dbus_bool_t +write_basic_field (DBusTypeWriter *writer, + int field, + int type, + const void *value) +{ + DBusTypeWriter sub; + DBusTypeWriter variant; + int start; + int padding; + unsigned char field_byte; + DBusString contained_type; + char buf[2]; + + start = writer->value_pos; + padding = _dbus_string_get_length (writer->value_str) - start; + + if (!_dbus_type_writer_recurse (writer, DBUS_TYPE_STRUCT, + NULL, 0, &sub)) + goto append_failed; + + field_byte = field; + if (!_dbus_type_writer_write_basic (&sub, DBUS_TYPE_BYTE, + &field_byte)) + goto append_failed; + + buf[0] = type; + buf[1] = '\0'; + _dbus_string_init_const_len (&contained_type, buf, 1); + + if (!_dbus_type_writer_recurse (&sub, DBUS_TYPE_VARIANT, + &contained_type, 0, &variant)) + goto append_failed; + + if (!_dbus_type_writer_write_basic (&variant, type, value)) + goto append_failed; + + if (!_dbus_type_writer_unrecurse (&sub, &variant)) + goto append_failed; + + if (!_dbus_type_writer_unrecurse (writer, &sub)) + goto append_failed; + + return TRUE; + + append_failed: + _dbus_string_delete (writer->value_str, + start, + _dbus_string_get_length (writer->value_str) - start - padding); + return FALSE; +} + +/** + * Sets a struct of { byte, variant } with the given basic type. + * + * @param reader the reader (should be iterating over the array pointing at the field to set) + * @param type the type of the value + * @param value the value as for _dbus_marshal_set_basic() + * @param realign_root where to realign from + * @returns #FALSE if no memory + */ +static dbus_bool_t +set_basic_field (DBusTypeReader *reader, + int field, + int type, + const void *value, + const DBusTypeReader *realign_root) +{ + DBusTypeReader sub; + DBusTypeReader variant; + + _dbus_type_reader_recurse (reader, &sub); + + _dbus_assert (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_BYTE); +#ifndef DBUS_DISABLE_ASSERT + { + unsigned char v_BYTE; + _dbus_type_reader_read_basic (&sub, &v_BYTE); + _dbus_assert (((int) v_BYTE) == field); + } +#endif + + if (!_dbus_type_reader_next (&sub)) + _dbus_assert_not_reached ("no variant field?"); + + _dbus_type_reader_recurse (&sub, &variant); + _dbus_assert (_dbus_type_reader_get_current_type (&variant) == type); + + if (!_dbus_type_reader_set_basic (&variant, value, realign_root)) + return FALSE; + + return TRUE; +} + +/** + * Gets the type of the message. + * + * @param header the header + * @returns the type + */ +int +_dbus_header_get_message_type (DBusHeader *header) +{ + int type; + + type = _dbus_string_get_byte (&header->data, TYPE_OFFSET); + _dbus_assert (type != DBUS_MESSAGE_TYPE_INVALID); + + return type; +} + +/** + * Sets the serial number of a header. This can only be done once on + * a header. + * + * @param header the header + * @param serial the serial + */ +void +_dbus_header_set_serial (DBusHeader *header, + dbus_uint32_t serial) +{ + /* we use this function to set the serial on outgoing + * messages, and to reset the serial in dbus_message_copy; + * this assertion should catch a double-set on outgoing. + */ + _dbus_assert (_dbus_header_get_serial (header) == 0 || + serial == 0); + + _dbus_marshal_set_uint32 (&header->data, + SERIAL_OFFSET, + serial, + header->byte_order); +} + +/** + * See dbus_message_get_serial() + * + * @param header the header + * @returns the client serial + */ +dbus_uint32_t +_dbus_header_get_serial (DBusHeader *header) +{ + return _dbus_marshal_read_uint32 (&header->data, + SERIAL_OFFSET, + header->byte_order, + NULL); +} + +/** + * Re-initializes a header that was previously initialized and never + * freed. After this, to make the header valid you have to call + * _dbus_header_create(). + * + * @param header header to re-initialize + * @param byte_order byte order of the header + */ +void +_dbus_header_reinit (DBusHeader *header, + int byte_order) +{ + _dbus_string_set_length (&header->data, 0); + + header->byte_order = byte_order; + header->padding = 0; + + _dbus_header_cache_invalidate_all (header); +} + +/** + * Initializes a header, but doesn't prepare it for use; + * to make the header valid, you have to call _dbus_header_create(). + * + * @param header header to initialize + * @param byte_order byte order of the header + * @returns #FALSE if not enough memory + */ +dbus_bool_t +_dbus_header_init (DBusHeader *header, + int byte_order) +{ + if (!_dbus_string_init_preallocated (&header->data, 32)) + return FALSE; + + _dbus_header_reinit (header, byte_order); + + return TRUE; +} + +/** + * Frees a header. + * + * @param header the header + */ +void +_dbus_header_free (DBusHeader *header) +{ + _dbus_string_free (&header->data); +} + +/** + * Initializes dest with a copy of the given header. + * Resets the message serial to 0 on the copy. + * + * @param header header to copy + * @param dest destination for copy + * @returns #FALSE if not enough memory + */ +dbus_bool_t +_dbus_header_copy (const DBusHeader *header, + DBusHeader *dest) +{ + *dest = *header; + + if (!_dbus_string_init_preallocated (&dest->data, + _dbus_string_get_length (&header->data))) + return FALSE; + + if (!_dbus_string_copy (&header->data, 0, &dest->data, 0)) + { + _dbus_string_free (&dest->data); + return FALSE; + } + + /* Reset the serial */ + _dbus_header_set_serial (dest, 0); + + return TRUE; +} + +/** + * Fills in the primary fields of the header, so the header is ready + * for use. #NULL may be specified for some or all of the fields to + * avoid adding those fields. Some combinations of fields don't make + * sense, and passing them in will trigger an assertion failure. + * + * @param header the header + * @param message_type the message type + * @param destination destination field or #NULL + * @param path path field or #NULL + * @param interface interface field or #NULL + * @param member member field or #NULL + * @param error_name error name or #NULL + * @returns #FALSE if not enough memory + */ +dbus_bool_t +_dbus_header_create (DBusHeader *header, + int message_type, + const char *destination, + const char *path, + const char *interface, + const char *member, + const char *error_name) +{ + unsigned char v_BYTE; + dbus_uint32_t v_UINT32; + DBusTypeWriter writer; + DBusTypeWriter array; + + _dbus_assert (((interface || message_type != DBUS_MESSAGE_TYPE_SIGNAL) && member) || + (error_name) || + !(interface || member || error_name)); + _dbus_assert (_dbus_string_get_length (&header->data) == 0); + + if (!reserve_header_padding (header)) + return FALSE; + + _dbus_type_writer_init_values_only (&writer, header->byte_order, + &_dbus_header_signature_str, 0, + &header->data, + HEADER_END_BEFORE_PADDING (header)); + + v_BYTE = header->byte_order; + if (!_dbus_type_writer_write_basic (&writer, DBUS_TYPE_BYTE, + &v_BYTE)) + goto oom; + + v_BYTE = message_type; + if (!_dbus_type_writer_write_basic (&writer, DBUS_TYPE_BYTE, + &v_BYTE)) + goto oom; + + v_BYTE = 0; /* flags */ + if (!_dbus_type_writer_write_basic (&writer, DBUS_TYPE_BYTE, + &v_BYTE)) + goto oom; + + v_BYTE = DBUS_MAJOR_PROTOCOL_VERSION; + if (!_dbus_type_writer_write_basic (&writer, DBUS_TYPE_BYTE, + &v_BYTE)) + goto oom; + + v_UINT32 = 0; /* body length */ + if (!_dbus_type_writer_write_basic (&writer, DBUS_TYPE_UINT32, + &v_UINT32)) + goto oom; + + v_UINT32 = 0; /* serial */ + if (!_dbus_type_writer_write_basic (&writer, DBUS_TYPE_UINT32, + &v_UINT32)) + goto oom; + + if (!_dbus_type_writer_recurse (&writer, DBUS_TYPE_ARRAY, + &_dbus_header_signature_str, + FIELDS_ARRAY_SIGNATURE_OFFSET, + &array)) + goto oom; + + /* Marshal all the fields (Marshall Fields?) */ + + if (path != NULL) + { + if (!write_basic_field (&array, + DBUS_HEADER_FIELD_PATH, + DBUS_TYPE_OBJECT_PATH, + &path)) + goto oom; + } + + if (destination != NULL) + { + if (!write_basic_field (&array, + DBUS_HEADER_FIELD_DESTINATION, + DBUS_TYPE_STRING, + &destination)) + goto oom; + } + + if (interface != NULL) + { + if (!write_basic_field (&array, + DBUS_HEADER_FIELD_INTERFACE, + DBUS_TYPE_STRING, + &interface)) + goto oom; + } + + if (member != NULL) + { + if (!write_basic_field (&array, + DBUS_HEADER_FIELD_MEMBER, + DBUS_TYPE_STRING, + &member)) + goto oom; + } + + if (error_name != NULL) + { + if (!write_basic_field (&array, + DBUS_HEADER_FIELD_ERROR_NAME, + DBUS_TYPE_STRING, + &error_name)) + goto oom; + } + + if (!_dbus_type_writer_unrecurse (&writer, &array)) + goto oom; + + correct_header_padding (header); + + return TRUE; + + oom: + _dbus_string_delete (&header->data, 0, + _dbus_string_get_length (&header->data) - header->padding); + correct_header_padding (header); + + return FALSE; +} + +/** + * Given data long enough to contain the length of the message body + * and the fields array, check whether the data is long enough to + * contain the entire message (assuming the claimed lengths are + * accurate). Also checks that the lengths are in sanity parameters. + * + * @param max_message_length maximum length of a valid message + * @param validity return location for why the data is invalid if it is + * @param byte_order return location for byte order + * @param fields_array_len return location for claimed fields array length + * @param header_len return location for claimed header length + * @param body_len return location for claimed body length + * @param str the data + * @param start start of data, 8-aligned + * @param len length of data + * @returns #TRUE if the data is long enough for the claimed length, and the lengths were valid + */ +dbus_bool_t +_dbus_header_have_message_untrusted (int max_message_length, + DBusValidity *validity, + int *byte_order, + int *fields_array_len, + int *header_len, + int *body_len, + const DBusString *str, + int start, + int len) + +{ + dbus_uint32_t header_len_unsigned; + dbus_uint32_t fields_array_len_unsigned; + dbus_uint32_t body_len_unsigned; + + _dbus_assert (start >= 0); + _dbus_assert (start < _DBUS_INT32_MAX / 2); + _dbus_assert (len >= 0); + + _dbus_assert (start == (int) _DBUS_ALIGN_VALUE (start, 8)); + + *byte_order = _dbus_string_get_byte (str, start + BYTE_ORDER_OFFSET); + + if (*byte_order != DBUS_LITTLE_ENDIAN && *byte_order != DBUS_BIG_ENDIAN) + { + *validity = DBUS_INVALID_BAD_BYTE_ORDER; + return FALSE; + } + + _dbus_assert (FIELDS_ARRAY_LENGTH_OFFSET + 4 <= len); + fields_array_len_unsigned = _dbus_marshal_read_uint32 (str, start + FIELDS_ARRAY_LENGTH_OFFSET, + *byte_order, NULL); + + if (fields_array_len_unsigned > (unsigned) max_message_length) + { + *validity = DBUS_INVALID_INSANE_FIELDS_ARRAY_LENGTH; + return FALSE; + } + + _dbus_assert (BODY_LENGTH_OFFSET + 4 < len); + body_len_unsigned = _dbus_marshal_read_uint32 (str, start + BODY_LENGTH_OFFSET, + *byte_order, NULL); + + if (body_len_unsigned > (unsigned) max_message_length) + { + *validity = DBUS_INVALID_INSANE_BODY_LENGTH; + return FALSE; + } + + header_len_unsigned = FIRST_FIELD_OFFSET + fields_array_len_unsigned; + header_len_unsigned = _DBUS_ALIGN_VALUE (header_len_unsigned, 8); + + /* overflow should be impossible since the lengths aren't allowed to + * be huge. + */ + _dbus_assert (max_message_length < _DBUS_INT32_MAX / 2); + if (body_len_unsigned + header_len_unsigned > (unsigned) max_message_length) + { + *validity = DBUS_INVALID_MESSAGE_TOO_LONG; + return FALSE; + } + + _dbus_assert (body_len_unsigned < (unsigned) _DBUS_INT32_MAX); + _dbus_assert (fields_array_len_unsigned < (unsigned) _DBUS_INT32_MAX); + _dbus_assert (header_len_unsigned < (unsigned) _DBUS_INT32_MAX); + + *body_len = body_len_unsigned; + *fields_array_len = fields_array_len_unsigned; + *header_len = header_len_unsigned; + + *validity = DBUS_VALID; + + _dbus_verbose ("have %d bytes, need body %u + header %u = %u\n", + len, body_len_unsigned, header_len_unsigned, + body_len_unsigned + header_len_unsigned); + + return (body_len_unsigned + header_len_unsigned) <= (unsigned) len; +} + +static DBusValidity +check_mandatory_fields (DBusHeader *header) +{ +#define REQUIRE_FIELD(name) do { if (header->fields[DBUS_HEADER_FIELD_##name].value_pos < 0) return DBUS_INVALID_MISSING_##name; } while (0) + + switch (_dbus_header_get_message_type (header)) + { + case DBUS_MESSAGE_TYPE_SIGNAL: + REQUIRE_FIELD (INTERFACE); + /* FALL THRU - signals also require the path and member */ + case DBUS_MESSAGE_TYPE_METHOD_CALL: + REQUIRE_FIELD (PATH); + REQUIRE_FIELD (MEMBER); + break; + case DBUS_MESSAGE_TYPE_ERROR: + REQUIRE_FIELD (ERROR_NAME); + REQUIRE_FIELD (REPLY_SERIAL); + break; + case DBUS_MESSAGE_TYPE_METHOD_RETURN: + REQUIRE_FIELD (REPLY_SERIAL); + break; + default: + /* other message types allowed but ignored */ + break; + } + + return DBUS_VALID; +} + +static DBusValidity +load_and_validate_field (DBusHeader *header, + int field, + DBusTypeReader *variant_reader) +{ + int type; + int expected_type; + const DBusString *value_str; + int value_pos; + int str_data_pos; + dbus_uint32_t v_UINT32; + int bad_string_code; + dbus_bool_t (* string_validation_func) (const DBusString *str, + int start, int len); + + /* Supposed to have been checked already */ + _dbus_assert (field <= DBUS_HEADER_FIELD_LAST); + _dbus_assert (field != DBUS_HEADER_FIELD_INVALID); + + /* Before we can cache a field, we need to know it has the right type */ + type = _dbus_type_reader_get_current_type (variant_reader); + + _dbus_assert (_dbus_header_field_types[field].code == field); + + expected_type = EXPECTED_TYPE_OF_FIELD (field); + if (type != expected_type) + { + _dbus_verbose ("Field %d should have type %d but has %d\n", + field, expected_type, type); + return DBUS_INVALID_HEADER_FIELD_HAS_WRONG_TYPE; + } + + /* If the field was provided twice, we aren't happy */ + if (header->fields[field].value_pos >= 0) + { + _dbus_verbose ("Header field %d seen a second time\n", field); + return DBUS_INVALID_HEADER_FIELD_APPEARS_TWICE; + } + + /* Now we can cache and look at the field content */ + _dbus_verbose ("initially caching field %d\n", field); + _dbus_header_cache_one (header, field, variant_reader); + + string_validation_func = NULL; + + /* make compiler happy that all this is initialized */ + v_UINT32 = 0; + value_str = NULL; + value_pos = -1; + str_data_pos = -1; + bad_string_code = DBUS_VALID; + + if (expected_type == DBUS_TYPE_UINT32) + { + _dbus_header_get_field_basic (header, field, expected_type, + &v_UINT32); + } + else if (expected_type == DBUS_TYPE_STRING || + expected_type == DBUS_TYPE_OBJECT_PATH || + expected_type == DBUS_TYPE_SIGNATURE) + { + _dbus_header_get_field_raw (header, field, + &value_str, &value_pos); + str_data_pos = _DBUS_ALIGN_VALUE (value_pos, 4) + 4; + } + else + { + _dbus_assert_not_reached ("none of the known fields should have this type"); + } + + switch (field) + { + case DBUS_HEADER_FIELD_DESTINATION: + string_validation_func = _dbus_validate_bus_name; + bad_string_code = DBUS_INVALID_BAD_DESTINATION; + break; + case DBUS_HEADER_FIELD_INTERFACE: + string_validation_func = _dbus_validate_interface; + bad_string_code = DBUS_INVALID_BAD_INTERFACE; + + if (_dbus_string_equal_substring (&_dbus_local_interface_str, + 0, + _dbus_string_get_length (&_dbus_local_interface_str), + value_str, str_data_pos)) + { + _dbus_verbose ("Message is on the local interface\n"); + return DBUS_INVALID_USES_LOCAL_INTERFACE; + } + break; + + case DBUS_HEADER_FIELD_MEMBER: + string_validation_func = _dbus_validate_member; + bad_string_code = DBUS_INVALID_BAD_MEMBER; + break; + + case DBUS_HEADER_FIELD_ERROR_NAME: + string_validation_func = _dbus_validate_error_name; + bad_string_code = DBUS_INVALID_BAD_ERROR_NAME; + break; + + case DBUS_HEADER_FIELD_SENDER: + string_validation_func = _dbus_validate_bus_name; + bad_string_code = DBUS_INVALID_BAD_SENDER; + break; + + case DBUS_HEADER_FIELD_PATH: + /* OBJECT_PATH was validated generically due to its type */ + string_validation_func = NULL; + + if (_dbus_string_equal_substring (&_dbus_local_path_str, + 0, + _dbus_string_get_length (&_dbus_local_path_str), + value_str, str_data_pos)) + { + _dbus_verbose ("Message is from the local path\n"); + return DBUS_INVALID_USES_LOCAL_PATH; + } + break; + + case DBUS_HEADER_FIELD_REPLY_SERIAL: + /* Can't be 0 */ + if (v_UINT32 == 0) + { + return DBUS_INVALID_BAD_SERIAL; + } + break; + + case DBUS_HEADER_FIELD_SIGNATURE: + /* SIGNATURE validated generically due to its type */ + string_validation_func = NULL; + break; + + default: + _dbus_assert_not_reached ("unknown field shouldn't be seen here"); + break; + } + + if (string_validation_func) + { + dbus_uint32_t len; + + _dbus_assert (bad_string_code != DBUS_VALID); + + len = _dbus_marshal_read_uint32 (value_str, value_pos, + header->byte_order, NULL); + +#if 0 + _dbus_verbose ("Validating string header field; code %d if fails\n", + bad_string_code); +#endif + if (!(*string_validation_func) (value_str, str_data_pos, len)) + return bad_string_code; + } + + return DBUS_VALID; +} + +/** + * Creates a message header from potentially-untrusted data. The + * return value is #TRUE if there was enough memory and the data was + * valid. If it returns #TRUE, the header will be created. If it + * returns #FALSE and *validity == #DBUS_VALIDITY_UNKNOWN_OOM_ERROR, + * then there wasn't enough memory. If it returns #FALSE + * and *validity != #DBUS_VALIDITY_UNKNOWN_OOM_ERROR then the data was + * invalid. + * + * The byte_order, fields_array_len, and body_len args should be from + * _dbus_header_have_message_untrusted(). Validation performed in + * _dbus_header_have_message_untrusted() is assumed to have been + * already done. + * + * @param header the header (must be initialized) + * @param mode whether to do validation + * @param validity return location for invalidity reason + * @param byte_order byte order from header + * @param fields_array_len claimed length of fields array + * @param body_len claimed length of body + * @param header_len claimed length of header + * @param str a string + * @param start start of header, 8-aligned + * @param len length of string to look at + * @returns #FALSE if no memory or data was invalid, #TRUE otherwise + */ +dbus_bool_t +_dbus_header_load (DBusHeader *header, + DBusValidationMode mode, + DBusValidity *validity, + int byte_order, + int fields_array_len, + int header_len, + int body_len, + const DBusString *str, + int start, + int len) +{ + int leftover; + DBusValidity v; + DBusTypeReader reader; + DBusTypeReader array_reader; + unsigned char v_byte; + dbus_uint32_t v_uint32; + dbus_uint32_t serial; + int padding_start; + int padding_len; + int i; + + _dbus_assert (start == (int) _DBUS_ALIGN_VALUE (start, 8)); + _dbus_assert (header_len <= len); + _dbus_assert (_dbus_string_get_length (&header->data) == 0); + + if (!_dbus_string_copy_len (str, start, header_len, &header->data, 0)) + { + _dbus_verbose ("Failed to copy buffer into new header\n"); + *validity = DBUS_VALIDITY_UNKNOWN_OOM_ERROR; + return FALSE; + } + + if (mode == DBUS_VALIDATION_MODE_WE_TRUST_THIS_DATA_ABSOLUTELY) + { + leftover = len - header_len - body_len - start; + } + else + { + v = _dbus_validate_body_with_reason (&_dbus_header_signature_str, 0, + byte_order, + &leftover, + str, start, len); + + if (v != DBUS_VALID) + { + *validity = v; + goto invalid; + } + } + + _dbus_assert (leftover < len); + + padding_len = header_len - (FIRST_FIELD_OFFSET + fields_array_len); + padding_start = start + FIRST_FIELD_OFFSET + fields_array_len; + _dbus_assert (start + header_len == (int) _DBUS_ALIGN_VALUE (padding_start, 8)); + _dbus_assert (start + header_len == padding_start + padding_len); + + if (mode != DBUS_VALIDATION_MODE_WE_TRUST_THIS_DATA_ABSOLUTELY) + { + if (!_dbus_string_validate_nul (str, padding_start, padding_len)) + { + *validity = DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL; + goto invalid; + } + } + + header->padding = padding_len; + + if (mode == DBUS_VALIDATION_MODE_WE_TRUST_THIS_DATA_ABSOLUTELY) + { + *validity = DBUS_VALID; + return TRUE; + } + + /* We now know the data is well-formed, but we have to check that + * it's valid. + */ + + _dbus_type_reader_init (&reader, + byte_order, + &_dbus_header_signature_str, 0, + str, start); + + /* BYTE ORDER */ + _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_BYTE); + _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == BYTE_ORDER_OFFSET); + _dbus_type_reader_read_basic (&reader, &v_byte); + _dbus_type_reader_next (&reader); + + _dbus_assert (v_byte == byte_order); + header->byte_order = byte_order; + + /* MESSAGE TYPE */ + _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_BYTE); + _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == TYPE_OFFSET); + _dbus_type_reader_read_basic (&reader, &v_byte); + _dbus_type_reader_next (&reader); + + /* unknown message types are supposed to be ignored, so only validation here is + * that it isn't invalid + */ + if (v_byte == DBUS_MESSAGE_TYPE_INVALID) + { + *validity = DBUS_INVALID_BAD_MESSAGE_TYPE; + goto invalid; + } + + /* FLAGS */ + _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_BYTE); + _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == FLAGS_OFFSET); + _dbus_type_reader_read_basic (&reader, &v_byte); + _dbus_type_reader_next (&reader); + + /* unknown flags should be ignored */ + + /* PROTOCOL VERSION */ + _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_BYTE); + _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == VERSION_OFFSET); + _dbus_type_reader_read_basic (&reader, &v_byte); + _dbus_type_reader_next (&reader); + + if (v_byte != DBUS_MAJOR_PROTOCOL_VERSION) + { + *validity = DBUS_INVALID_BAD_PROTOCOL_VERSION; + goto invalid; + } + + /* BODY LENGTH */ + _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_UINT32); + _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == BODY_LENGTH_OFFSET); + _dbus_type_reader_read_basic (&reader, &v_uint32); + _dbus_type_reader_next (&reader); + + _dbus_assert (body_len == (signed) v_uint32); + + /* SERIAL */ + _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_UINT32); + _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == SERIAL_OFFSET); + _dbus_type_reader_read_basic (&reader, &serial); + _dbus_type_reader_next (&reader); + + if (serial == 0) + { + *validity = DBUS_INVALID_BAD_SERIAL; + goto invalid; + } + + _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_ARRAY); + _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == FIELDS_ARRAY_LENGTH_OFFSET); + + _dbus_type_reader_recurse (&reader, &array_reader); + while (_dbus_type_reader_get_current_type (&array_reader) != DBUS_TYPE_INVALID) + { + DBusTypeReader struct_reader; + DBusTypeReader variant_reader; + unsigned char field_code; + + _dbus_assert (_dbus_type_reader_get_current_type (&array_reader) == DBUS_TYPE_STRUCT); + + _dbus_type_reader_recurse (&array_reader, &struct_reader); + + _dbus_assert (_dbus_type_reader_get_current_type (&struct_reader) == DBUS_TYPE_BYTE); + _dbus_type_reader_read_basic (&struct_reader, &field_code); + _dbus_type_reader_next (&struct_reader); + + if (field_code == DBUS_HEADER_FIELD_INVALID) + { + _dbus_verbose ("invalid header field code\n"); + *validity = DBUS_INVALID_HEADER_FIELD_CODE; + goto invalid; + } + + if (field_code > DBUS_HEADER_FIELD_LAST) + { + _dbus_verbose ("unknown header field code %d, skipping\n", + field_code); + goto next_field; + } + + _dbus_assert (_dbus_type_reader_get_current_type (&struct_reader) == DBUS_TYPE_VARIANT); + _dbus_type_reader_recurse (&struct_reader, &variant_reader); + + v = load_and_validate_field (header, field_code, &variant_reader); + if (v != DBUS_VALID) + { + _dbus_verbose ("Field %d was invalid\n", field_code); + *validity = v; + goto invalid; + } + + next_field: + _dbus_type_reader_next (&array_reader); + } + + /* Anything we didn't fill in is now known not to exist */ + i = 0; + while (i <= DBUS_HEADER_FIELD_LAST) + { + if (header->fields[i].value_pos == _DBUS_HEADER_FIELD_VALUE_UNKNOWN) + header->fields[i].value_pos = _DBUS_HEADER_FIELD_VALUE_NONEXISTENT; + ++i; + } + + v = check_mandatory_fields (header); + if (v != DBUS_VALID) + { + _dbus_verbose ("Mandatory fields were missing, code %d\n", v); + *validity = v; + goto invalid; + } + + *validity = DBUS_VALID; + return TRUE; + + invalid: + _dbus_string_set_length (&header->data, 0); + return FALSE; +} + +/** + * Fills in the correct body length. + * + * @param header the header + * @param body_len the length of the body + */ +void +_dbus_header_update_lengths (DBusHeader *header, + int body_len) +{ + _dbus_marshal_set_uint32 (&header->data, + BODY_LENGTH_OFFSET, + body_len, + header->byte_order); +} + +static dbus_bool_t +find_field_for_modification (DBusHeader *header, + int field, + DBusTypeReader *reader, + DBusTypeReader *realign_root) +{ + dbus_bool_t retval; + + retval = FALSE; + + _dbus_type_reader_init (realign_root, + header->byte_order, + &_dbus_header_signature_str, + FIELDS_ARRAY_SIGNATURE_OFFSET, + &header->data, + FIELDS_ARRAY_LENGTH_OFFSET); + + _dbus_type_reader_recurse (realign_root, reader); + + while (_dbus_type_reader_get_current_type (reader) != DBUS_TYPE_INVALID) + { + DBusTypeReader sub; + unsigned char field_code; + + _dbus_type_reader_recurse (reader, &sub); + + _dbus_assert (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_BYTE); + _dbus_type_reader_read_basic (&sub, &field_code); + + if (field_code == (unsigned) field) + { + _dbus_assert (_dbus_type_reader_get_current_type (reader) == DBUS_TYPE_STRUCT); + retval = TRUE; + goto done; + } + + _dbus_type_reader_next (reader); + } + + done: + return retval; +} + +/** + * Sets the value of a field with basic type. If the value is a string + * value, it isn't allowed to be #NULL. If the field doesn't exist, + * it will be created. + * + * @param header the header + * @param field the field to set + * @param type the type of the value + * @param value the value as for _dbus_marshal_set_basic() + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_header_set_field_basic (DBusHeader *header, + int field, + int type, + const void *value) +{ + _dbus_assert (field <= DBUS_HEADER_FIELD_LAST); + + if (!reserve_header_padding (header)) + return FALSE; + + /* If the field exists we set, otherwise we append */ + if (_dbus_header_cache_check (header, field)) + { + DBusTypeReader reader; + DBusTypeReader realign_root; + + if (!find_field_for_modification (header, field, + &reader, &realign_root)) + _dbus_assert_not_reached ("field was marked present in cache but wasn't found"); + + if (!set_basic_field (&reader, field, type, value, &realign_root)) + return FALSE; + } + else + { + DBusTypeWriter writer; + DBusTypeWriter array; + + _dbus_type_writer_init_values_only (&writer, + header->byte_order, + &_dbus_header_signature_str, + FIELDS_ARRAY_SIGNATURE_OFFSET, + &header->data, + FIELDS_ARRAY_LENGTH_OFFSET); + + /* recurse into array without creating a new length, and jump to + * end of array. + */ + if (!_dbus_type_writer_append_array (&writer, + &_dbus_header_signature_str, + FIELDS_ARRAY_ELEMENT_SIGNATURE_OFFSET, + &array)) + _dbus_assert_not_reached ("recurse into ARRAY should not have used memory"); + + _dbus_assert (array.u.array.len_pos == FIELDS_ARRAY_LENGTH_OFFSET); + _dbus_assert (array.u.array.start_pos == FIRST_FIELD_OFFSET); + _dbus_assert (array.value_pos == HEADER_END_BEFORE_PADDING (header)); + + if (!write_basic_field (&array, + field, type, value)) + return FALSE; + + if (!_dbus_type_writer_unrecurse (&writer, &array)) + _dbus_assert_not_reached ("unrecurse from ARRAY should not have used memory"); + } + + correct_header_padding (header); + + /* We could be smarter about this (only invalidate fields after the + * one we modified, or even only if the one we modified changed + * length). But this hack is a start. + */ + _dbus_header_cache_invalidate_all (header); + + return TRUE; +} + +/** + * Gets the value of a field with basic type. If the field + * doesn't exist, returns #FALSE, otherwise returns #TRUE. + * + * @param header the header + * @param field the field to get + * @param type the type of the value + * @param value the value as for _dbus_marshal_read_basic() + * @returns #FALSE if the field doesn't exist + */ +dbus_bool_t +_dbus_header_get_field_basic (DBusHeader *header, + int field, + int type, + void *value) +{ + _dbus_assert (field != DBUS_HEADER_FIELD_INVALID); + _dbus_assert (field <= DBUS_HEADER_FIELD_LAST); + _dbus_assert (_dbus_header_field_types[field].code == field); + /* in light of this you might ask why the type is passed in; + * the only rationale I can think of is so the caller has + * to specify its expectation and breaks if we change it + */ + _dbus_assert (type == EXPECTED_TYPE_OF_FIELD (field)); + + if (!_dbus_header_cache_check (header, field)) + return FALSE; + + _dbus_assert (header->fields[field].value_pos >= 0); + + _dbus_marshal_read_basic (&header->data, + header->fields[field].value_pos, + type, value, header->byte_order, + NULL); + + return TRUE; +} + +/** + * Gets the raw marshaled data for a field. If the field doesn't + * exist, returns #FALSE, otherwise returns #TRUE. Returns the start + * of the marshaled data, i.e. usually the byte where the length + * starts (for strings and arrays) or for basic types just the value + * itself. + * + * @param header the header + * @param field the field to get + * @param str return location for the data string + * @param pos return location for start of field value + * @returns #FALSE if the field doesn't exist + */ +dbus_bool_t +_dbus_header_get_field_raw (DBusHeader *header, + int field, + const DBusString **str, + int *pos) +{ + if (!_dbus_header_cache_check (header, field)) + return FALSE; + + if (str) + *str = &header->data; + if (pos) + *pos = header->fields[field].value_pos; + + return TRUE; +} + +/** + * Deletes a field, if it exists. + * + * @param header the header + * @param field the field to delete + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_header_delete_field (DBusHeader *header, + int field) +{ + DBusTypeReader reader; + DBusTypeReader realign_root; + + if (_dbus_header_cache_known_nonexistent (header, field)) + return TRUE; /* nothing to do */ + + /* Scan to the field we want, delete and realign, reappend + * padding. Field may turn out not to exist. + */ + if (!find_field_for_modification (header, field, + &reader, &realign_root)) + return TRUE; /* nothing to do */ + + if (!reserve_header_padding (header)) + return FALSE; + + if (!_dbus_type_reader_delete (&reader, + &realign_root)) + return FALSE; + + correct_header_padding (header); + + _dbus_header_cache_invalidate_all (header); + + _dbus_assert (!_dbus_header_cache_check (header, field)); /* Expensive assertion ... */ + + return TRUE; +} + +/** + * Toggles a message flag bit, turning on the bit if value = TRUE and + * flipping it off if value = FALSE. + * + * @param header the header + * @param flag the message flag to toggle + * @param value toggle on or off + */ +void +_dbus_header_toggle_flag (DBusHeader *header, + dbus_uint32_t flag, + dbus_bool_t value) +{ + unsigned char *flags_p; + + flags_p = _dbus_string_get_data_len (&header->data, FLAGS_OFFSET, 1); + + if (value) + *flags_p |= flag; + else + *flags_p &= ~flag; +} + +/** + * Gets a message flag bit, returning TRUE if the bit is set. + * + * @param header the header + * @param flag the message flag to get + * @returns #TRUE if the flag is set + */ +dbus_bool_t +_dbus_header_get_flag (DBusHeader *header, + dbus_uint32_t flag) +{ + const unsigned char *flags_p; + + flags_p = _dbus_string_get_const_data_len (&header->data, FLAGS_OFFSET, 1); + + return (*flags_p & flag) != 0; +} + +/** + * Swaps the header into the given order if required. + * + * @param header the header + * @param new_order the new byte order + */ +void +_dbus_header_byteswap (DBusHeader *header, + int new_order) +{ + if (header->byte_order == new_order) + return; + + _dbus_marshal_byteswap (&_dbus_header_signature_str, + 0, header->byte_order, + new_order, + &header->data, 0); + + header->byte_order = new_order; +} + +/** @} */ + +#ifdef DBUS_BUILD_TESTS +#include "dbus-test.h" +#include + +dbus_bool_t +_dbus_marshal_header_test (void) +{ + + return TRUE; +} + +#endif /* DBUS_BUILD_TESTS */ diff --git a/src/dbus/dbus-marshal-header.h b/src/dbus/dbus-marshal-header.h new file mode 100644 index 0000000..52b6c73 --- /dev/null +++ b/src/dbus/dbus-marshal-header.h @@ -0,0 +1,133 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-marshal-header.h Managing marshaling/demarshaling of message headers + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef DBUS_MARSHAL_HEADER_H +#define DBUS_MARSHAL_HEADER_H + +#include +#include +#include + +#ifndef PACKAGE +#error "config.h not included here" +#endif + +typedef struct DBusHeader DBusHeader; +typedef struct DBusHeaderField DBusHeaderField; + +#define _DBUS_HEADER_FIELD_VALUE_UNKNOWN -1 +#define _DBUS_HEADER_FIELD_VALUE_NONEXISTENT -2 + +/** + * Cached information about a header field in the message + */ +struct DBusHeaderField +{ + int value_pos; /**< Position of field value, or -1/-2 */ +}; + +/** + * Message header data and some cached details of it. + */ +struct DBusHeader +{ + DBusString data; /**< Header network data, stored + * separately from body so we can + * independently realloc it. + */ + + DBusHeaderField fields[DBUS_HEADER_FIELD_LAST + 1]; /**< Track the location + * of each field in header + */ + + dbus_uint32_t padding : 3; /**< bytes of alignment in header */ + dbus_uint32_t byte_order : 8; /**< byte order of header */ +}; + +dbus_bool_t _dbus_header_init (DBusHeader *header, + int byte_order); +void _dbus_header_free (DBusHeader *header); +void _dbus_header_reinit (DBusHeader *header, + int byte_order); +dbus_bool_t _dbus_header_create (DBusHeader *header, + int type, + const char *destination, + const char *path, + const char *interface, + const char *member, + const char *error_name); +dbus_bool_t _dbus_header_copy (const DBusHeader *header, + DBusHeader *dest); +int _dbus_header_get_message_type (DBusHeader *header); +void _dbus_header_set_serial (DBusHeader *header, + dbus_uint32_t serial); +dbus_uint32_t _dbus_header_get_serial (DBusHeader *header); +void _dbus_header_update_lengths (DBusHeader *header, + int body_len); +dbus_bool_t _dbus_header_set_field_basic (DBusHeader *header, + int field, + int type, + const void *value); +dbus_bool_t _dbus_header_get_field_basic (DBusHeader *header, + int field, + int type, + void *value); +dbus_bool_t _dbus_header_get_field_raw (DBusHeader *header, + int field, + const DBusString **str, + int *pos); +dbus_bool_t _dbus_header_delete_field (DBusHeader *header, + int field); +void _dbus_header_toggle_flag (DBusHeader *header, + dbus_uint32_t flag, + dbus_bool_t value); +dbus_bool_t _dbus_header_get_flag (DBusHeader *header, + dbus_uint32_t flag); +dbus_bool_t _dbus_header_ensure_signature (DBusHeader *header, + DBusString **type_str, + int *type_pos); +dbus_bool_t _dbus_header_have_message_untrusted (int max_message_length, + DBusValidity *validity, + int *byte_order, + int *fields_array_len, + int *header_len, + int *body_len, + const DBusString *str, + int start, + int len); +dbus_bool_t _dbus_header_load (DBusHeader *header, + DBusValidationMode mode, + DBusValidity *validity, + int byte_order, + int fields_array_len, + int header_len, + int body_len, + const DBusString *str, + int start, + int len); +void _dbus_header_byteswap (DBusHeader *header, + int new_order); + + + +#endif /* DBUS_MARSHAL_HEADER_H */ diff --git a/src/dbus/dbus-marshal-recursive-util.c b/src/dbus/dbus-marshal-recursive-util.c new file mode 100644 index 0000000..c8ae93d --- /dev/null +++ b/src/dbus/dbus-marshal-recursive-util.c @@ -0,0 +1,3565 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-marshal-recursive-util.c Would be in dbus-marshal-recursive.c, but only used in bus/tests + * + * Copyright (C) 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include + +#ifdef DBUS_BUILD_TESTS + +#include "dbus-marshal-recursive.h" +#include "dbus-marshal-basic.h" +#include "dbus-signature.h" +#include "dbus-internals.h" +#include + +static void +basic_value_zero (DBusBasicValue *value) +{ + +#ifdef DBUS_HAVE_INT64 + value->u64 = 0; +#else + value->u64.first32 = 0; + value->u64.second32 = 0; +#endif +} + +static dbus_bool_t +basic_value_equal (int type, + DBusBasicValue *lhs, + DBusBasicValue *rhs) +{ + if (type == DBUS_TYPE_STRING || + type == DBUS_TYPE_SIGNATURE || + type == DBUS_TYPE_OBJECT_PATH) + { + return strcmp (lhs->str, rhs->str) == 0; + } + else + { +#ifdef DBUS_HAVE_INT64 + return lhs->u64 == rhs->u64; +#else + return lhs->u64.first32 == rhs->u64.first32 && + lhs->u64.second32 == rhs->u64.second32; +#endif + } +} + +static dbus_bool_t +equal_values_helper (DBusTypeReader *lhs, + DBusTypeReader *rhs) +{ + int lhs_type; + int rhs_type; + + lhs_type = _dbus_type_reader_get_current_type (lhs); + rhs_type = _dbus_type_reader_get_current_type (rhs); + + if (lhs_type != rhs_type) + return FALSE; + + if (lhs_type == DBUS_TYPE_INVALID) + return TRUE; + + if (dbus_type_is_basic (lhs_type)) + { + DBusBasicValue lhs_value; + DBusBasicValue rhs_value; + + basic_value_zero (&lhs_value); + basic_value_zero (&rhs_value); + + _dbus_type_reader_read_basic (lhs, &lhs_value); + _dbus_type_reader_read_basic (rhs, &rhs_value); + + return basic_value_equal (lhs_type, &lhs_value, &rhs_value); + } + else + { + DBusTypeReader lhs_sub; + DBusTypeReader rhs_sub; + + _dbus_type_reader_recurse (lhs, &lhs_sub); + _dbus_type_reader_recurse (rhs, &rhs_sub); + + return equal_values_helper (&lhs_sub, &rhs_sub); + } +} + +/** + * See whether the two readers point to identical data blocks. + * + * @param lhs reader 1 + * @param rhs reader 2 + * @returns #TRUE if the data blocks have the same values + */ +dbus_bool_t +_dbus_type_reader_equal_values (const DBusTypeReader *lhs, + const DBusTypeReader *rhs) +{ + DBusTypeReader copy_lhs = *lhs; + DBusTypeReader copy_rhs = *rhs; + + return equal_values_helper (©_lhs, ©_rhs); +} + +/* TESTS */ + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +#include "dbus-test.h" +#include "dbus-list.h" +#include +#include + +/* Whether to do the OOM stuff (only with other expensive tests) */ +#define TEST_OOM_HANDLING 0 +/* We do start offset 0 through 9, to get various alignment cases. Still this + * obviously makes the test suite run 10x as slow. + */ +#define MAX_INITIAL_OFFSET 9 + +/* Largest iteration count to test copying, realignment, + * etc. with. i.e. we only test this stuff with some of the smaller + * data sets. + */ +#define MAX_ITERATIONS_FOR_EXPENSIVE_TESTS 1000 + +typedef struct +{ + int byte_order; + int initial_offset; + DBusString signature; + DBusString body; +} DataBlock; + +typedef struct +{ + int saved_sig_len; + int saved_body_len; +} DataBlockState; + +#define N_FENCE_BYTES 5 +#define FENCE_BYTES_STR "abcde" +#define INITIAL_PADDING_BYTE '\0' + +static dbus_bool_t +data_block_init (DataBlock *block, + int byte_order, + int initial_offset) +{ + if (!_dbus_string_init (&block->signature)) + return FALSE; + + if (!_dbus_string_init (&block->body)) + { + _dbus_string_free (&block->signature); + return FALSE; + } + + if (!_dbus_string_insert_bytes (&block->signature, 0, initial_offset, + INITIAL_PADDING_BYTE) || + !_dbus_string_insert_bytes (&block->body, 0, initial_offset, + INITIAL_PADDING_BYTE) || + !_dbus_string_append (&block->signature, FENCE_BYTES_STR) || + !_dbus_string_append (&block->body, FENCE_BYTES_STR)) + { + _dbus_string_free (&block->signature); + _dbus_string_free (&block->body); + return FALSE; + } + + block->byte_order = byte_order; + block->initial_offset = initial_offset; + + return TRUE; +} + +static void +data_block_save (DataBlock *block, + DataBlockState *state) +{ + state->saved_sig_len = _dbus_string_get_length (&block->signature) - N_FENCE_BYTES; + state->saved_body_len = _dbus_string_get_length (&block->body) - N_FENCE_BYTES; +} + +static void +data_block_restore (DataBlock *block, + DataBlockState *state) +{ + _dbus_string_delete (&block->signature, + state->saved_sig_len, + _dbus_string_get_length (&block->signature) - state->saved_sig_len - N_FENCE_BYTES); + _dbus_string_delete (&block->body, + state->saved_body_len, + _dbus_string_get_length (&block->body) - state->saved_body_len - N_FENCE_BYTES); +} + +static void +data_block_verify (DataBlock *block) +{ + if (!_dbus_string_ends_with_c_str (&block->signature, + FENCE_BYTES_STR)) + { + int offset; + + offset = _dbus_string_get_length (&block->signature) - N_FENCE_BYTES - 8; + if (offset < 0) + offset = 0; + + _dbus_verbose_bytes_of_string (&block->signature, + offset, + _dbus_string_get_length (&block->signature) - offset); + _dbus_assert_not_reached ("block did not verify: bad bytes at end of signature"); + } + if (!_dbus_string_ends_with_c_str (&block->body, + FENCE_BYTES_STR)) + { + int offset; + + offset = _dbus_string_get_length (&block->body) - N_FENCE_BYTES - 8; + if (offset < 0) + offset = 0; + + _dbus_verbose_bytes_of_string (&block->body, + offset, + _dbus_string_get_length (&block->body) - offset); + _dbus_assert_not_reached ("block did not verify: bad bytes at end of body"); + } + + _dbus_assert (_dbus_string_validate_nul (&block->signature, + 0, block->initial_offset)); + _dbus_assert (_dbus_string_validate_nul (&block->body, + 0, block->initial_offset)); +} + +static void +data_block_free (DataBlock *block) +{ + data_block_verify (block); + + _dbus_string_free (&block->signature); + _dbus_string_free (&block->body); +} + +static void +data_block_reset (DataBlock *block) +{ + data_block_verify (block); + + _dbus_string_delete (&block->signature, + block->initial_offset, + _dbus_string_get_length (&block->signature) - N_FENCE_BYTES - block->initial_offset); + _dbus_string_delete (&block->body, + block->initial_offset, + _dbus_string_get_length (&block->body) - N_FENCE_BYTES - block->initial_offset); + + data_block_verify (block); +} + +static void +data_block_init_reader_writer (DataBlock *block, + DBusTypeReader *reader, + DBusTypeWriter *writer) +{ + if (reader) + _dbus_type_reader_init (reader, + block->byte_order, + &block->signature, + block->initial_offset, + &block->body, + block->initial_offset); + + if (writer) + _dbus_type_writer_init (writer, + block->byte_order, + &block->signature, + _dbus_string_get_length (&block->signature) - N_FENCE_BYTES, + &block->body, + _dbus_string_get_length (&block->body) - N_FENCE_BYTES); +} + +static void +real_check_expected_type (DBusTypeReader *reader, + int expected, + const char *funcname, + int line) +{ + int t; + + t = _dbus_type_reader_get_current_type (reader); + + if (t != expected) + { + _dbus_warn ("Read type %s while expecting %s at %s line %d\n", + _dbus_type_to_string (t), + _dbus_type_to_string (expected), + funcname, line); + + _dbus_assert_not_reached ("read wrong type"); + } +} + +#define check_expected_type(reader, expected) real_check_expected_type (reader, expected, _DBUS_FUNCTION_NAME, __LINE__) + +#define NEXT_EXPECTING_TRUE(reader) do { if (!_dbus_type_reader_next (reader)) \ + { \ + _dbus_warn ("_dbus_type_reader_next() should have returned TRUE at %s %d\n", \ + _DBUS_FUNCTION_NAME, __LINE__); \ + _dbus_assert_not_reached ("test failed"); \ + } \ +} while (0) + +#define NEXT_EXPECTING_FALSE(reader) do { if (_dbus_type_reader_next (reader)) \ + { \ + _dbus_warn ("_dbus_type_reader_next() should have returned FALSE at %s %d\n", \ + _DBUS_FUNCTION_NAME, __LINE__); \ + _dbus_assert_not_reached ("test failed"); \ + } \ + check_expected_type (reader, DBUS_TYPE_INVALID); \ +} while (0) + +typedef struct TestTypeNode TestTypeNode; +typedef struct TestTypeNodeClass TestTypeNodeClass; +typedef struct TestTypeNodeContainer TestTypeNodeContainer; +typedef struct TestTypeNodeContainerClass TestTypeNodeContainerClass; + +struct TestTypeNode +{ + const TestTypeNodeClass *klass; +}; + +struct TestTypeNodeContainer +{ + TestTypeNode base; + DBusList *children; +}; + +struct TestTypeNodeClass +{ + int typecode; + + int instance_size; + + int subclass_detail; /* a bad hack to avoid a bunch of subclass casting */ + + dbus_bool_t (* construct) (TestTypeNode *node); + void (* destroy) (TestTypeNode *node); + + dbus_bool_t (* write_value) (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed); + dbus_bool_t (* read_value) (TestTypeNode *node, + DBusTypeReader *reader, + int seed); + dbus_bool_t (* set_value) (TestTypeNode *node, + DBusTypeReader *reader, + DBusTypeReader *realign_root, + int seed); + dbus_bool_t (* build_signature) (TestTypeNode *node, + DBusString *str); + dbus_bool_t (* write_multi) (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed, + int count); + dbus_bool_t (* read_multi) (TestTypeNode *node, + DBusTypeReader *reader, + int seed, + int count); +}; + +struct TestTypeNodeContainerClass +{ + TestTypeNodeClass base; +}; + +/* FIXME this could be chilled out substantially by unifying + * the basic types into basic_write_value/basic_read_value + * and by merging read_value and set_value into one function + * taking a flag argument. + */ +static dbus_bool_t int16_write_value (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed); +static dbus_bool_t int16_read_value (TestTypeNode *node, + DBusTypeReader *reader, + int seed); +static dbus_bool_t int16_set_value (TestTypeNode *node, + DBusTypeReader *reader, + DBusTypeReader *realign_root, + int seed); +static dbus_bool_t int16_write_multi (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed, + int count); +static dbus_bool_t int16_read_multi (TestTypeNode *node, + DBusTypeReader *reader, + int seed, + int count); +static dbus_bool_t int32_write_value (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed); +static dbus_bool_t int32_read_value (TestTypeNode *node, + DBusTypeReader *reader, + int seed); +static dbus_bool_t int32_set_value (TestTypeNode *node, + DBusTypeReader *reader, + DBusTypeReader *realign_root, + int seed); +static dbus_bool_t int32_write_multi (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed, + int count); +static dbus_bool_t int32_read_multi (TestTypeNode *node, + DBusTypeReader *reader, + int seed, + int count); +static dbus_bool_t int64_write_value (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed); +static dbus_bool_t int64_read_value (TestTypeNode *node, + DBusTypeReader *reader, + int seed); +static dbus_bool_t int64_set_value (TestTypeNode *node, + DBusTypeReader *reader, + DBusTypeReader *realign_root, + int seed); +static dbus_bool_t string_write_value (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed); +static dbus_bool_t string_read_value (TestTypeNode *node, + DBusTypeReader *reader, + int seed); +static dbus_bool_t string_set_value (TestTypeNode *node, + DBusTypeReader *reader, + DBusTypeReader *realign_root, + int seed); +static dbus_bool_t bool_write_value (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed); +static dbus_bool_t bool_read_value (TestTypeNode *node, + DBusTypeReader *reader, + int seed); +static dbus_bool_t bool_set_value (TestTypeNode *node, + DBusTypeReader *reader, + DBusTypeReader *realign_root, + int seed); +static dbus_bool_t byte_write_value (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed); +static dbus_bool_t byte_read_value (TestTypeNode *node, + DBusTypeReader *reader, + int seed); +static dbus_bool_t byte_set_value (TestTypeNode *node, + DBusTypeReader *reader, + DBusTypeReader *realign_root, + int seed); +static dbus_bool_t double_write_value (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed); +static dbus_bool_t double_read_value (TestTypeNode *node, + DBusTypeReader *reader, + int seed); +static dbus_bool_t double_set_value (TestTypeNode *node, + DBusTypeReader *reader, + DBusTypeReader *realign_root, + int seed); +static dbus_bool_t object_path_write_value (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed); +static dbus_bool_t object_path_read_value (TestTypeNode *node, + DBusTypeReader *reader, + int seed); +static dbus_bool_t object_path_set_value (TestTypeNode *node, + DBusTypeReader *reader, + DBusTypeReader *realign_root, + int seed); +static dbus_bool_t signature_write_value (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed); +static dbus_bool_t signature_read_value (TestTypeNode *node, + DBusTypeReader *reader, + int seed); +static dbus_bool_t signature_set_value (TestTypeNode *node, + DBusTypeReader *reader, + DBusTypeReader *realign_root, + int seed); +static dbus_bool_t struct_write_value (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed); +static dbus_bool_t struct_read_value (TestTypeNode *node, + DBusTypeReader *reader, + int seed); +static dbus_bool_t struct_set_value (TestTypeNode *node, + DBusTypeReader *reader, + DBusTypeReader *realign_root, + int seed); +static dbus_bool_t struct_build_signature (TestTypeNode *node, + DBusString *str); +static dbus_bool_t dict_write_value (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed); +static dbus_bool_t dict_read_value (TestTypeNode *node, + DBusTypeReader *reader, + int seed); +static dbus_bool_t dict_set_value (TestTypeNode *node, + DBusTypeReader *reader, + DBusTypeReader *realign_root, + int seed); +static dbus_bool_t dict_build_signature (TestTypeNode *node, + DBusString *str); +static dbus_bool_t array_write_value (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed); +static dbus_bool_t array_read_value (TestTypeNode *node, + DBusTypeReader *reader, + int seed); +static dbus_bool_t array_set_value (TestTypeNode *node, + DBusTypeReader *reader, + DBusTypeReader *realign_root, + int seed); +static dbus_bool_t array_build_signature (TestTypeNode *node, + DBusString *str); +static dbus_bool_t variant_write_value (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed); +static dbus_bool_t variant_read_value (TestTypeNode *node, + DBusTypeReader *reader, + int seed); +static dbus_bool_t variant_set_value (TestTypeNode *node, + DBusTypeReader *reader, + DBusTypeReader *realign_root, + int seed); +static void container_destroy (TestTypeNode *node); + + + +static const TestTypeNodeClass int16_class = { + DBUS_TYPE_INT16, + sizeof (TestTypeNode), + 0, + NULL, + NULL, + int16_write_value, + int16_read_value, + int16_set_value, + NULL, + int16_write_multi, + int16_read_multi +}; + +static const TestTypeNodeClass uint16_class = { + DBUS_TYPE_UINT16, + sizeof (TestTypeNode), + 0, + NULL, + NULL, + int16_write_value, /* recycle from int16 */ + int16_read_value, /* recycle from int16 */ + int16_set_value, /* recycle from int16 */ + NULL, + int16_write_multi, /* recycle from int16 */ + int16_read_multi /* recycle from int16 */ +}; + +static const TestTypeNodeClass int32_class = { + DBUS_TYPE_INT32, + sizeof (TestTypeNode), + 0, + NULL, + NULL, + int32_write_value, + int32_read_value, + int32_set_value, + NULL, + int32_write_multi, + int32_read_multi +}; + +static const TestTypeNodeClass uint32_class = { + DBUS_TYPE_UINT32, + sizeof (TestTypeNode), + 0, + NULL, + NULL, + int32_write_value, /* recycle from int32 */ + int32_read_value, /* recycle from int32 */ + int32_set_value, /* recycle from int32 */ + NULL, + int32_write_multi, /* recycle from int32 */ + int32_read_multi /* recycle from int32 */ +}; + +static const TestTypeNodeClass int64_class = { + DBUS_TYPE_INT64, + sizeof (TestTypeNode), + 0, + NULL, + NULL, + int64_write_value, + int64_read_value, + int64_set_value, + NULL, + NULL, /* FIXME */ + NULL /* FIXME */ +}; + +static const TestTypeNodeClass uint64_class = { + DBUS_TYPE_UINT64, + sizeof (TestTypeNode), + 0, + NULL, + NULL, + int64_write_value, /* recycle from int64 */ + int64_read_value, /* recycle from int64 */ + int64_set_value, /* recycle from int64 */ + NULL, + NULL, /* FIXME */ + NULL /* FIXME */ +}; + +static const TestTypeNodeClass string_0_class = { + DBUS_TYPE_STRING, + sizeof (TestTypeNode), + 0, /* string length */ + NULL, + NULL, + string_write_value, + string_read_value, + string_set_value, + NULL, + NULL, + NULL +}; + +static const TestTypeNodeClass string_1_class = { + DBUS_TYPE_STRING, + sizeof (TestTypeNode), + 1, /* string length */ + NULL, + NULL, + string_write_value, + string_read_value, + string_set_value, + NULL, + NULL, + NULL +}; + +/* with nul, a len 3 string should fill 4 bytes and thus is "special" */ +static const TestTypeNodeClass string_3_class = { + DBUS_TYPE_STRING, + sizeof (TestTypeNode), + 3, /* string length */ + NULL, + NULL, + string_write_value, + string_read_value, + string_set_value, + NULL, + NULL, + NULL +}; + +/* with nul, a len 8 string should fill 9 bytes and thus is "special" (far-fetched I suppose) */ +static const TestTypeNodeClass string_8_class = { + DBUS_TYPE_STRING, + sizeof (TestTypeNode), + 8, /* string length */ + NULL, + NULL, + string_write_value, + string_read_value, + string_set_value, + NULL, + NULL, + NULL +}; + +static const TestTypeNodeClass bool_class = { + DBUS_TYPE_BOOLEAN, + sizeof (TestTypeNode), + 0, + NULL, + NULL, + bool_write_value, + bool_read_value, + bool_set_value, + NULL, + NULL, /* FIXME */ + NULL /* FIXME */ +}; + +static const TestTypeNodeClass byte_class = { + DBUS_TYPE_BYTE, + sizeof (TestTypeNode), + 0, + NULL, + NULL, + byte_write_value, + byte_read_value, + byte_set_value, + NULL, + NULL, /* FIXME */ + NULL /* FIXME */ +}; + +static const TestTypeNodeClass double_class = { + DBUS_TYPE_DOUBLE, + sizeof (TestTypeNode), + 0, + NULL, + NULL, + double_write_value, + double_read_value, + double_set_value, + NULL, + NULL, /* FIXME */ + NULL /* FIXME */ +}; + +static const TestTypeNodeClass object_path_class = { + DBUS_TYPE_OBJECT_PATH, + sizeof (TestTypeNode), + 0, + NULL, + NULL, + object_path_write_value, + object_path_read_value, + object_path_set_value, + NULL, + NULL, + NULL +}; + +static const TestTypeNodeClass signature_class = { + DBUS_TYPE_SIGNATURE, + sizeof (TestTypeNode), + 0, + NULL, + NULL, + signature_write_value, + signature_read_value, + signature_set_value, + NULL, + NULL, + NULL +}; + +static const TestTypeNodeClass struct_1_class = { + DBUS_TYPE_STRUCT, + sizeof (TestTypeNodeContainer), + 1, /* number of times children appear as fields */ + NULL, + container_destroy, + struct_write_value, + struct_read_value, + struct_set_value, + struct_build_signature, + NULL, + NULL +}; + +static const TestTypeNodeClass struct_2_class = { + DBUS_TYPE_STRUCT, + sizeof (TestTypeNodeContainer), + 2, /* number of times children appear as fields */ + NULL, + container_destroy, + struct_write_value, + struct_read_value, + struct_set_value, + struct_build_signature, + NULL, + NULL +}; + +static const TestTypeNodeClass dict_1_class = { + DBUS_TYPE_ARRAY, /* this is correct, a dict is an array of dict entry */ + sizeof (TestTypeNodeContainer), + 1, /* number of entries */ + NULL, + container_destroy, + dict_write_value, + dict_read_value, + dict_set_value, + dict_build_signature, + NULL, + NULL +}; + +static dbus_bool_t arrays_write_fixed_in_blocks = FALSE; + +static const TestTypeNodeClass array_0_class = { + DBUS_TYPE_ARRAY, + sizeof (TestTypeNodeContainer), + 0, /* number of array elements */ + NULL, + container_destroy, + array_write_value, + array_read_value, + array_set_value, + array_build_signature, + NULL, + NULL +}; + +static const TestTypeNodeClass array_1_class = { + DBUS_TYPE_ARRAY, + sizeof (TestTypeNodeContainer), + 1, /* number of array elements */ + NULL, + container_destroy, + array_write_value, + array_read_value, + array_set_value, + array_build_signature, + NULL, + NULL +}; + +static const TestTypeNodeClass array_2_class = { + DBUS_TYPE_ARRAY, + sizeof (TestTypeNodeContainer), + 2, /* number of array elements */ + NULL, + container_destroy, + array_write_value, + array_read_value, + array_set_value, + array_build_signature, + NULL, + NULL +}; + +static const TestTypeNodeClass array_9_class = { + DBUS_TYPE_ARRAY, + sizeof (TestTypeNodeContainer), + 9, /* number of array elements */ + NULL, + container_destroy, + array_write_value, + array_read_value, + array_set_value, + array_build_signature, + NULL, + NULL +}; + +static const TestTypeNodeClass variant_class = { + DBUS_TYPE_VARIANT, + sizeof (TestTypeNodeContainer), + 0, + NULL, + container_destroy, + variant_write_value, + variant_read_value, + variant_set_value, + NULL, + NULL, + NULL +}; + +static const TestTypeNodeClass* const +basic_nodes[] = { + &int16_class, + &uint16_class, + &int32_class, + &uint32_class, + &int64_class, + &uint64_class, + &bool_class, + &byte_class, + &double_class, + &string_0_class, + &string_1_class, + &string_3_class, + &string_8_class, + &object_path_class, + &signature_class +}; +#define N_BASICS (_DBUS_N_ELEMENTS (basic_nodes)) + +static const TestTypeNodeClass* const +container_nodes[] = { + &struct_1_class, + &array_1_class, + &struct_2_class, + &array_0_class, + &array_2_class, + &variant_class, + &dict_1_class /* last since we want struct and array before it */ + /* array_9_class is omitted on purpose, it's too slow; + * we only use it in one hardcoded test below + */ +}; +#define N_CONTAINERS (_DBUS_N_ELEMENTS (container_nodes)) + +static TestTypeNode* +node_new (const TestTypeNodeClass *klass) +{ + TestTypeNode *node; + + node = dbus_malloc0 (klass->instance_size); + if (node == NULL) + return NULL; + + node->klass = klass; + + if (klass->construct) + { + if (!(* klass->construct) (node)) + { + dbus_free (node); + return NULL; + } + } + + return node; +} + +static void +node_destroy (TestTypeNode *node) +{ + if (node->klass->destroy) + (* node->klass->destroy) (node); + dbus_free (node); +} + +static dbus_bool_t +node_write_value (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed) +{ + dbus_bool_t retval; + + retval = (* node->klass->write_value) (node, block, writer, seed); + +#if 0 + /* Handy to see where things break, but too expensive to do all the time */ + data_block_verify (block); +#endif + + return retval; +} + +static dbus_bool_t +node_read_value (TestTypeNode *node, + DBusTypeReader *reader, + int seed) +{ + /* DBusTypeReader restored; */ + + if (!(* node->klass->read_value) (node, reader, seed)) + return FALSE; + + return TRUE; +} + +/* Warning: if this one fails due to OOM, it has side effects (can + * modify only some of the sub-values). OK in a test suite, but we + * never do this in real code. + */ +static dbus_bool_t +node_set_value (TestTypeNode *node, + DBusTypeReader *reader, + DBusTypeReader *realign_root, + int seed) +{ + if (!(* node->klass->set_value) (node, reader, realign_root, seed)) + return FALSE; + + return TRUE; +} + +static dbus_bool_t +node_build_signature (TestTypeNode *node, + DBusString *str) +{ + if (node->klass->build_signature) + return (* node->klass->build_signature) (node, str); + else + return _dbus_string_append_byte (str, node->klass->typecode); +} + +static dbus_bool_t +node_append_child (TestTypeNode *node, + TestTypeNode *child) +{ + TestTypeNodeContainer *container = (TestTypeNodeContainer*) node; + + _dbus_assert (node->klass->instance_size >= (int) sizeof (TestTypeNodeContainer)); + + if (!_dbus_list_append (&container->children, child)) + _dbus_assert_not_reached ("no memory"); /* we never check the return value on node_append_child anyhow - it's run from outside the malloc-failure test code */ + + return TRUE; +} + +static dbus_bool_t +node_write_multi (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed, + int n_copies) +{ + dbus_bool_t retval; + + _dbus_assert (node->klass->write_multi != NULL); + retval = (* node->klass->write_multi) (node, block, writer, seed, n_copies); + +#if 0 + /* Handy to see where things break, but too expensive to do all the time */ + data_block_verify (block); +#endif + + return retval; +} + +static dbus_bool_t +node_read_multi (TestTypeNode *node, + DBusTypeReader *reader, + int seed, + int n_copies) +{ + _dbus_assert (node->klass->read_multi != NULL); + + if (!(* node->klass->read_multi) (node, reader, seed, n_copies)) + return FALSE; + + return TRUE; +} + +static int n_iterations_completed_total = 0; +static int n_iterations_completed_this_test = 0; +static int n_iterations_expected_this_test = 0; + +typedef struct +{ + const DBusString *signature; + DataBlock *block; + int type_offset; + TestTypeNode **nodes; + int n_nodes; +} NodeIterationData; + +static dbus_bool_t +run_test_copy (NodeIterationData *nid) +{ + DataBlock *src; + DataBlock dest; + dbus_bool_t retval; + DBusTypeReader reader; + DBusTypeWriter writer; + + _dbus_verbose ("%s\n", _DBUS_FUNCTION_NAME); + + src = nid->block; + + retval = FALSE; + + if (!data_block_init (&dest, src->byte_order, src->initial_offset)) + return FALSE; + + data_block_init_reader_writer (src, &reader, NULL); + data_block_init_reader_writer (&dest, NULL, &writer); + + /* DBusTypeWriter assumes it's writing into an existing signature, + * so doesn't add nul on its own. We have to do that. + */ + if (!_dbus_string_insert_byte (&dest.signature, + dest.initial_offset, '\0')) + goto out; + + if (!_dbus_type_writer_write_reader (&writer, &reader)) + goto out; + + /* Data blocks should now be identical */ + if (!_dbus_string_equal (&src->signature, &dest.signature)) + { + _dbus_verbose ("SOURCE\n"); + _dbus_verbose_bytes_of_string (&src->signature, 0, + _dbus_string_get_length (&src->signature)); + _dbus_verbose ("DEST\n"); + _dbus_verbose_bytes_of_string (&dest.signature, 0, + _dbus_string_get_length (&dest.signature)); + _dbus_assert_not_reached ("signatures did not match"); + } + + if (!_dbus_string_equal (&src->body, &dest.body)) + { + _dbus_verbose ("SOURCE\n"); + _dbus_verbose_bytes_of_string (&src->body, 0, + _dbus_string_get_length (&src->body)); + _dbus_verbose ("DEST\n"); + _dbus_verbose_bytes_of_string (&dest.body, 0, + _dbus_string_get_length (&dest.body)); + _dbus_assert_not_reached ("bodies did not match"); + } + + retval = TRUE; + + out: + + data_block_free (&dest); + + return retval; +} + +static dbus_bool_t +run_test_values_only_write (NodeIterationData *nid) +{ + DBusTypeReader reader; + DBusTypeWriter writer; + int i; + dbus_bool_t retval; + int sig_len; + + _dbus_verbose ("%s\n", _DBUS_FUNCTION_NAME); + + retval = FALSE; + + data_block_reset (nid->block); + + sig_len = _dbus_string_get_length (nid->signature); + + _dbus_type_writer_init_values_only (&writer, + nid->block->byte_order, + nid->signature, 0, + &nid->block->body, + _dbus_string_get_length (&nid->block->body) - N_FENCE_BYTES); + _dbus_type_reader_init (&reader, + nid->block->byte_order, + nid->signature, 0, + &nid->block->body, + nid->block->initial_offset); + + i = 0; + while (i < nid->n_nodes) + { + if (!node_write_value (nid->nodes[i], nid->block, &writer, i)) + goto out; + + ++i; + } + + /* if we wrote any typecodes then this would fail */ + _dbus_assert (sig_len == _dbus_string_get_length (nid->signature)); + + /* But be sure we wrote out the values correctly */ + i = 0; + while (i < nid->n_nodes) + { + if (!node_read_value (nid->nodes[i], &reader, i)) + goto out; + + if (i + 1 == nid->n_nodes) + NEXT_EXPECTING_FALSE (&reader); + else + NEXT_EXPECTING_TRUE (&reader); + + ++i; + } + + retval = TRUE; + + out: + data_block_reset (nid->block); + return retval; +} + +/* offset the seed for setting, so we set different numbers than + * we originally wrote. Don't offset by a huge number since in + * some cases it's value = possibilities[seed % n_possibilities] + * and we don't want to wrap around. bool_from_seed + * is just seed % 2 even. + */ +#define SET_SEED 1 +static dbus_bool_t +run_test_set_values (NodeIterationData *nid) +{ + DBusTypeReader reader; + DBusTypeReader realign_root; + dbus_bool_t retval; + int i; + + _dbus_verbose ("%s\n", _DBUS_FUNCTION_NAME); + + retval = FALSE; + + data_block_init_reader_writer (nid->block, + &reader, NULL); + + realign_root = reader; + + i = 0; + while (i < nid->n_nodes) + { + if (!node_set_value (nid->nodes[i], + &reader, &realign_root, + i + SET_SEED)) + goto out; + + if (i + 1 == nid->n_nodes) + NEXT_EXPECTING_FALSE (&reader); + else + NEXT_EXPECTING_TRUE (&reader); + + ++i; + } + + /* Check that the new values were set */ + + reader = realign_root; + + i = 0; + while (i < nid->n_nodes) + { + if (!node_read_value (nid->nodes[i], &reader, + i + SET_SEED)) + goto out; + + if (i + 1 == nid->n_nodes) + NEXT_EXPECTING_FALSE (&reader); + else + NEXT_EXPECTING_TRUE (&reader); + + ++i; + } + + retval = TRUE; + + out: + return retval; +} + +static dbus_bool_t +run_test_delete_values (NodeIterationData *nid) +{ + DBusTypeReader reader; + dbus_bool_t retval; + int t; + + _dbus_verbose ("%s\n", _DBUS_FUNCTION_NAME); + + retval = FALSE; + + data_block_init_reader_writer (nid->block, + &reader, NULL); + + while ((t = _dbus_type_reader_get_current_type (&reader)) != DBUS_TYPE_INVALID) + { + /* Right now, deleting only works on array elements. We delete + * all array elements, and then verify that there aren't any + * left. + */ + if (t == DBUS_TYPE_ARRAY) + { + DBusTypeReader array; + int n_elements; + int elem_type; + + _dbus_type_reader_recurse (&reader, &array); + n_elements = 0; + while (_dbus_type_reader_get_current_type (&array) != DBUS_TYPE_INVALID) + { + n_elements += 1; + _dbus_type_reader_next (&array); + } + + /* reset to start of array */ + _dbus_type_reader_recurse (&reader, &array); + _dbus_verbose ("recursing into deletion loop reader.value_pos = %d array.value_pos = %d array.u.start_pos = %d\n", + reader.value_pos, array.value_pos, array.u.array.start_pos); + while ((elem_type = _dbus_type_reader_get_current_type (&array)) != DBUS_TYPE_INVALID) + { + /* We don't want to always delete from the same part of the array. */ + static int cycle = 0; + int elem; + + _dbus_assert (n_elements > 0); + + elem = cycle; + if (elem == 3 || elem >= n_elements) /* end of array */ + elem = n_elements - 1; + + _dbus_verbose ("deleting array element %d of %d type %s cycle %d reader pos %d elem pos %d\n", + elem, n_elements, _dbus_type_to_string (elem_type), + cycle, reader.value_pos, array.value_pos); + while (elem > 0) + { + if (!_dbus_type_reader_next (&array)) + _dbus_assert_not_reached ("should have had another element\n"); + --elem; + } + + if (!_dbus_type_reader_delete (&array, &reader)) + goto out; + + n_elements -= 1; + + /* reset */ + _dbus_type_reader_recurse (&reader, &array); + + if (cycle > 2) + cycle = 0; + else + cycle += 1; + } + } + _dbus_type_reader_next (&reader); + } + + /* Check that there are no array elements left */ + data_block_init_reader_writer (nid->block, + &reader, NULL); + + while ((t = _dbus_type_reader_get_current_type (&reader)) != DBUS_TYPE_INVALID) + { + _dbus_type_reader_next (&reader); + } + + retval = TRUE; + + out: + return retval; +} + +static dbus_bool_t +run_test_nodes_iteration (void *data) +{ + NodeIterationData *nid = data; + DBusTypeReader reader; + DBusTypeWriter writer; + int i; + dbus_bool_t retval; + + /* Stuff to do: + * 1. write the value + * 2. strcmp-compare with the signature we built + * 3. read the value + * 4. type-iterate the signature and the value and see if they are the same type-wise + */ + retval = FALSE; + + data_block_init_reader_writer (nid->block, + &reader, &writer); + + /* DBusTypeWriter assumes it's writing into an existing signature, + * so doesn't add nul on its own. We have to do that. + */ + if (!_dbus_string_insert_byte (&nid->block->signature, + nid->type_offset, '\0')) + goto out; + + i = 0; + while (i < nid->n_nodes) + { + if (!node_write_value (nid->nodes[i], nid->block, &writer, i)) + goto out; + + ++i; + } + + if (!_dbus_string_equal_substring (nid->signature, 0, _dbus_string_get_length (nid->signature), + &nid->block->signature, nid->type_offset)) + { + _dbus_warn ("Expected signature '%s' and got '%s' with initial offset %d\n", + _dbus_string_get_const_data (nid->signature), + _dbus_string_get_const_data_len (&nid->block->signature, nid->type_offset, 0), + nid->type_offset); + _dbus_assert_not_reached ("wrong signature"); + } + + i = 0; + while (i < nid->n_nodes) + { + if (!node_read_value (nid->nodes[i], &reader, i)) + goto out; + + if (i + 1 == nid->n_nodes) + NEXT_EXPECTING_FALSE (&reader); + else + NEXT_EXPECTING_TRUE (&reader); + + ++i; + } + + if (n_iterations_expected_this_test <= MAX_ITERATIONS_FOR_EXPENSIVE_TESTS) + { + /* this set values test uses code from copy and + * values_only_write so would ideally be last so you get a + * simpler test case for problems with copying or values_only + * writing; but it also needs an already-written DataBlock so it + * has to go first. Comment it out if it breaks, and see if the + * later tests also break - debug them first if so. + */ + if (!run_test_set_values (nid)) + goto out; + + if (!run_test_delete_values (nid)) + goto out; + + if (!run_test_copy (nid)) + goto out; + + if (!run_test_values_only_write (nid)) + goto out; + } + + /* FIXME type-iterate both signature and value and compare the resulting + * tree to the node tree perhaps + */ + + retval = TRUE; + + out: + + data_block_reset (nid->block); + + return retval; +} + +static void +run_test_nodes_in_one_configuration (TestTypeNode **nodes, + int n_nodes, + const DBusString *signature, + int byte_order, + int initial_offset) +{ + DataBlock block; + NodeIterationData nid; + + if (!data_block_init (&block, byte_order, initial_offset)) + _dbus_assert_not_reached ("no memory"); + + nid.signature = signature; + nid.block = █ + nid.type_offset = initial_offset; + nid.nodes = nodes; + nid.n_nodes = n_nodes; + + if (TEST_OOM_HANDLING && + n_iterations_expected_this_test <= MAX_ITERATIONS_FOR_EXPENSIVE_TESTS) + { + _dbus_test_oom_handling ("running test node", + run_test_nodes_iteration, + &nid); + } + else + { + if (!run_test_nodes_iteration (&nid)) + _dbus_assert_not_reached ("no memory"); + } + + data_block_free (&block); +} + +static void +run_test_nodes (TestTypeNode **nodes, + int n_nodes) +{ + int i; + DBusString signature; + + if (!_dbus_string_init (&signature)) + _dbus_assert_not_reached ("no memory"); + + i = 0; + while (i < n_nodes) + { + if (! node_build_signature (nodes[i], &signature)) + _dbus_assert_not_reached ("no memory"); + + ++i; + } + + _dbus_verbose (">>> test nodes with signature '%s'\n", + _dbus_string_get_const_data (&signature)); + + i = 0; + while (i <= MAX_INITIAL_OFFSET) + { + run_test_nodes_in_one_configuration (nodes, n_nodes, &signature, + DBUS_LITTLE_ENDIAN, i); + run_test_nodes_in_one_configuration (nodes, n_nodes, &signature, + DBUS_BIG_ENDIAN, i); + + ++i; + } + + n_iterations_completed_this_test += 1; + n_iterations_completed_total += 1; + + if (n_iterations_completed_this_test == n_iterations_expected_this_test) + { + fprintf (stderr, " 100%% %d this test (%d cumulative)\n", + n_iterations_completed_this_test, + n_iterations_completed_total); + } + /* this happens to turn out well with mod == 1 */ + else if ((n_iterations_completed_this_test % + (int)(n_iterations_expected_this_test / 10.0)) == 1) + { + fprintf (stderr, " %d%% ", (int) (n_iterations_completed_this_test / (double) n_iterations_expected_this_test * 100)); + } + + _dbus_string_free (&signature); +} + +#define N_VALUES (N_BASICS * N_CONTAINERS + N_BASICS) + +static TestTypeNode* +value_generator (int *ip) +{ + int i = *ip; + const TestTypeNodeClass *child_klass; + const TestTypeNodeClass *container_klass; + TestTypeNode *child; + TestTypeNode *node; + + _dbus_assert (i <= N_VALUES); + + if (i == N_VALUES) + { + return NULL; + } + else if (i < N_BASICS) + { + node = node_new (basic_nodes[i]); + } + else + { + /* imagine an array: + * container 0 of basic 0 + * container 0 of basic 1 + * container 0 of basic 2 + * container 1 of basic 0 + * container 1 of basic 1 + * container 1 of basic 2 + */ + i -= N_BASICS; + + container_klass = container_nodes[i / N_BASICS]; + child_klass = basic_nodes[i % N_BASICS]; + + node = node_new (container_klass); + child = node_new (child_klass); + + node_append_child (node, child); + } + + *ip += 1; /* increment the generator */ + + return node; +} + +static void +build_body (TestTypeNode **nodes, + int n_nodes, + int byte_order, + DBusString *signature, + DBusString *body) +{ + int i; + DataBlock block; + DBusTypeReader reader; + DBusTypeWriter writer; + + i = 0; + while (i < n_nodes) + { + if (! node_build_signature (nodes[i], signature)) + _dbus_assert_not_reached ("no memory"); + + ++i; + } + + if (!data_block_init (&block, byte_order, 0)) + _dbus_assert_not_reached ("no memory"); + + data_block_init_reader_writer (&block, + &reader, &writer); + + /* DBusTypeWriter assumes it's writing into an existing signature, + * so doesn't add nul on its own. We have to do that. + */ + if (!_dbus_string_insert_byte (&block.signature, + 0, '\0')) + _dbus_assert_not_reached ("no memory"); + + i = 0; + while (i < n_nodes) + { + if (!node_write_value (nodes[i], &block, &writer, i)) + _dbus_assert_not_reached ("no memory"); + + ++i; + } + + if (!_dbus_string_copy_len (&block.body, 0, + _dbus_string_get_length (&block.body) - N_FENCE_BYTES, + body, 0)) + _dbus_assert_not_reached ("oom"); + + data_block_free (&block); +} + +dbus_bool_t +dbus_internal_do_not_use_generate_bodies (int sequence, + int byte_order, + DBusString *signature, + DBusString *body) +{ + TestTypeNode *nodes[1]; + int i; + int n_nodes; + + nodes[0] = value_generator (&sequence); + + if (nodes[0] == NULL) + return FALSE; + + n_nodes = 1; + + build_body (nodes, n_nodes, byte_order, signature, body); + + + i = 0; + while (i < n_nodes) + { + node_destroy (nodes[i]); + ++i; + } + + return TRUE; +} + +static void +make_and_run_values_inside_container (const TestTypeNodeClass *container_klass, + int n_nested) +{ + TestTypeNode *root; + TestTypeNode *container; + TestTypeNode *child; + int i; + + root = node_new (container_klass); + container = root; + for (i = 1; i < n_nested; i++) + { + child = node_new (container_klass); + node_append_child (container, child); + container = child; + } + + /* container should now be the most-nested container */ + + i = 0; + while ((child = value_generator (&i))) + { + node_append_child (container, child); + + run_test_nodes (&root, 1); + + _dbus_list_clear (&((TestTypeNodeContainer*)container)->children); + node_destroy (child); + } + + node_destroy (root); +} + +static void +start_next_test (const char *format, + int expected) +{ + n_iterations_completed_this_test = 0; + n_iterations_expected_this_test = expected; + + fprintf (stderr, ">>> >>> "); + fprintf (stderr, format, + n_iterations_expected_this_test); +} + +static void +make_and_run_test_nodes (void) +{ + int i, j, k, m; + + /* We try to do this in order of "complicatedness" so that test + * failures tend to show up in the simplest test case that + * demonstrates the failure. There are also some tests that run + * more than once for this reason, first while going through simple + * cases, second while going through a broader range of complex + * cases. + */ + /* Each basic node. The basic nodes should include: + * + * - each fixed-size type (in such a way that it has different values each time, + * so we can tell if we mix two of them up) + * - strings of various lengths + * - object path + * - signature + */ + /* Each container node. The container nodes should include: + * + * struct with 1 and 2 copies of the contained item + * array with 0, 1, 2 copies of the contained item + * variant + */ + /* Let a "value" be a basic node, or a container containing a single basic node. + * Let n_values be the number of such values i.e. (n_container * n_basic + n_basic) + * When iterating through all values to make combinations, do the basic types + * first and the containers second. + */ + /* Each item is shown with its number of iterations to complete so + * we can keep a handle on this unit test + */ + + /* FIXME test just an empty body, no types at all */ + + start_next_test ("Each value by itself %d iterations\n", N_VALUES); + { + TestTypeNode *node; + i = 0; + while ((node = value_generator (&i))) + { + run_test_nodes (&node, 1); + + node_destroy (node); + } + } + + start_next_test ("Each value by itself with arrays as blocks %d iterations\n", N_VALUES); + arrays_write_fixed_in_blocks = TRUE; + { + TestTypeNode *node; + i = 0; + while ((node = value_generator (&i))) + { + run_test_nodes (&node, 1); + + node_destroy (node); + } + } + arrays_write_fixed_in_blocks = FALSE; + + start_next_test ("All values in one big toplevel %d iteration\n", 1); + { + TestTypeNode *nodes[N_VALUES]; + + i = 0; + while ((nodes[i] = value_generator (&i))) + ; + + run_test_nodes (nodes, N_VALUES); + + for (i = 0; i < N_VALUES; i++) + node_destroy (nodes[i]); + } + + start_next_test ("Each value,value pair combination as toplevel, in both orders %d iterations\n", + N_VALUES * N_VALUES); + { + TestTypeNode *nodes[2]; + + i = 0; + while ((nodes[0] = value_generator (&i))) + { + j = 0; + while ((nodes[1] = value_generator (&j))) + { + run_test_nodes (nodes, 2); + + node_destroy (nodes[1]); + } + + node_destroy (nodes[0]); + } + } + + start_next_test ("Each container containing each value %d iterations\n", + N_CONTAINERS * N_VALUES); + for (i = 0; i < N_CONTAINERS; i++) + { + const TestTypeNodeClass *container_klass = container_nodes[i]; + + make_and_run_values_inside_container (container_klass, 1); + } + + start_next_test ("Each container containing each value with arrays as blocks %d iterations\n", + N_CONTAINERS * N_VALUES); + arrays_write_fixed_in_blocks = TRUE; + for (i = 0; i < N_CONTAINERS; i++) + { + const TestTypeNodeClass *container_klass = container_nodes[i]; + + make_and_run_values_inside_container (container_klass, 1); + } + arrays_write_fixed_in_blocks = FALSE; + + start_next_test ("Each container of same container of each value %d iterations\n", + N_CONTAINERS * N_VALUES); + for (i = 0; i < N_CONTAINERS; i++) + { + const TestTypeNodeClass *container_klass = container_nodes[i]; + + make_and_run_values_inside_container (container_klass, 2); + } + + start_next_test ("Each container of same container of same container of each value %d iterations\n", + N_CONTAINERS * N_VALUES); + for (i = 0; i < N_CONTAINERS; i++) + { + const TestTypeNodeClass *container_klass = container_nodes[i]; + + make_and_run_values_inside_container (container_klass, 3); + } + + start_next_test ("Each value,value pair inside a struct %d iterations\n", + N_VALUES * N_VALUES); + { + TestTypeNode *val1, *val2; + TestTypeNode *node; + + node = node_new (&struct_1_class); + + i = 0; + while ((val1 = value_generator (&i))) + { + j = 0; + while ((val2 = value_generator (&j))) + { + TestTypeNodeContainer *container = (TestTypeNodeContainer*) node; + + node_append_child (node, val1); + node_append_child (node, val2); + + run_test_nodes (&node, 1); + + _dbus_list_clear (&container->children); + node_destroy (val2); + } + node_destroy (val1); + } + node_destroy (node); + } + + start_next_test ("All values in one big struct %d iteration\n", + 1); + { + TestTypeNode *node; + TestTypeNode *child; + + node = node_new (&struct_1_class); + + i = 0; + while ((child = value_generator (&i))) + node_append_child (node, child); + + run_test_nodes (&node, 1); + + node_destroy (node); + } + + start_next_test ("Each value in a large array %d iterations\n", + N_VALUES); + { + TestTypeNode *val; + TestTypeNode *node; + + node = node_new (&array_9_class); + + i = 0; + while ((val = value_generator (&i))) + { + TestTypeNodeContainer *container = (TestTypeNodeContainer*) node; + + node_append_child (node, val); + + run_test_nodes (&node, 1); + + _dbus_list_clear (&container->children); + node_destroy (val); + } + + node_destroy (node); + } + + start_next_test ("Each container of each container of each value %d iterations\n", + N_CONTAINERS * N_CONTAINERS * N_VALUES); + for (i = 0; i < N_CONTAINERS; i++) + { + const TestTypeNodeClass *outer_container_klass = container_nodes[i]; + TestTypeNode *outer_container = node_new (outer_container_klass); + + for (j = 0; j < N_CONTAINERS; j++) + { + TestTypeNode *child; + const TestTypeNodeClass *inner_container_klass = container_nodes[j]; + TestTypeNode *inner_container = node_new (inner_container_klass); + + node_append_child (outer_container, inner_container); + + m = 0; + while ((child = value_generator (&m))) + { + node_append_child (inner_container, child); + + run_test_nodes (&outer_container, 1); + + _dbus_list_clear (&((TestTypeNodeContainer*)inner_container)->children); + node_destroy (child); + } + _dbus_list_clear (&((TestTypeNodeContainer*)outer_container)->children); + node_destroy (inner_container); + } + node_destroy (outer_container); + } + + start_next_test ("Each container of each container of each container of each value %d iterations\n", + N_CONTAINERS * N_CONTAINERS * N_CONTAINERS * N_VALUES); + for (i = 0; i < N_CONTAINERS; i++) + { + const TestTypeNodeClass *outer_container_klass = container_nodes[i]; + TestTypeNode *outer_container = node_new (outer_container_klass); + + for (j = 0; j < N_CONTAINERS; j++) + { + const TestTypeNodeClass *inner_container_klass = container_nodes[j]; + TestTypeNode *inner_container = node_new (inner_container_klass); + + node_append_child (outer_container, inner_container); + + for (k = 0; k < N_CONTAINERS; k++) + { + TestTypeNode *child; + const TestTypeNodeClass *center_container_klass = container_nodes[k]; + TestTypeNode *center_container = node_new (center_container_klass); + + node_append_child (inner_container, center_container); + + m = 0; + while ((child = value_generator (&m))) + { + node_append_child (center_container, child); + + run_test_nodes (&outer_container, 1); + + _dbus_list_clear (&((TestTypeNodeContainer*)center_container)->children); + node_destroy (child); + } + _dbus_list_clear (&((TestTypeNodeContainer*)inner_container)->children); + node_destroy (center_container); + } + _dbus_list_clear (&((TestTypeNodeContainer*)outer_container)->children); + node_destroy (inner_container); + } + node_destroy (outer_container); + } + +#if 0 + /* This one takes a really long time, so comment it out for now */ + start_next_test ("Each value,value,value triplet combination as toplevel, in all orders %d iterations\n", + N_VALUES * N_VALUES * N_VALUES); + { + TestTypeNode *nodes[3]; + + i = 0; + while ((nodes[0] = value_generator (&i))) + { + j = 0; + while ((nodes[1] = value_generator (&j))) + { + k = 0; + while ((nodes[2] = value_generator (&k))) + { + run_test_nodes (nodes, 3); + + node_destroy (nodes[2]); + } + node_destroy (nodes[1]); + } + node_destroy (nodes[0]); + } + } +#endif /* #if 0 expensive test */ + + fprintf (stderr, "%d total iterations of recursive marshaling tests\n", + n_iterations_completed_total); + fprintf (stderr, "each iteration ran at initial offsets 0 through %d in both big and little endian\n", + MAX_INITIAL_OFFSET); + fprintf (stderr, "out of memory handling %s tested\n", + TEST_OOM_HANDLING ? "was" : "was not"); +} + +dbus_bool_t +_dbus_marshal_recursive_test (void) +{ + make_and_run_test_nodes (); + + return TRUE; +} + +/* + * + * + * Implementations of each type node class + * + * + * + */ +#define MAX_MULTI_COUNT 5 + +#define SAMPLE_INT16 1234 +#define SAMPLE_INT16_ALTERNATE 6785 +static dbus_int16_t +int16_from_seed (int seed) +{ + /* Generate an integer value that's predictable from seed. We could + * just use seed itself, but that would only ever touch one byte of + * the int so would miss some kinds of bug. + */ + dbus_int16_t v; + + v = 42; /* just to quiet compiler afaik */ + switch (seed % 5) + { + case 0: + v = SAMPLE_INT16; + break; + case 1: + v = SAMPLE_INT16_ALTERNATE; + break; + case 2: + v = -1; + break; + case 3: + v = _DBUS_INT16_MAX; + break; + case 4: + v = 1; + break; + } + + if (seed > 1) + v *= seed; /* wraps around eventually, which is fine */ + + return v; +} + +static dbus_bool_t +int16_write_value (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed) +{ + /* also used for uint16 */ + dbus_int16_t v; + + v = int16_from_seed (seed); + + return _dbus_type_writer_write_basic (writer, + node->klass->typecode, + &v); +} + +static dbus_bool_t +int16_read_value (TestTypeNode *node, + DBusTypeReader *reader, + int seed) +{ + /* also used for uint16 */ + dbus_int16_t v; + + check_expected_type (reader, node->klass->typecode); + + _dbus_type_reader_read_basic (reader, + (dbus_int16_t*) &v); + + _dbus_assert (v == int16_from_seed (seed)); + + return TRUE; +} + +static dbus_bool_t +int16_set_value (TestTypeNode *node, + DBusTypeReader *reader, + DBusTypeReader *realign_root, + int seed) +{ + /* also used for uint16 */ + dbus_int16_t v; + + v = int16_from_seed (seed); + + return _dbus_type_reader_set_basic (reader, + &v, + realign_root); +} + +static dbus_bool_t +int16_write_multi (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed, + int count) +{ + /* also used for uint16 */ + dbus_int16_t values[MAX_MULTI_COUNT]; + dbus_int16_t *v_ARRAY_INT16 = values; + int i; + + for (i = 0; i < count; ++i) + values[i] = int16_from_seed (seed + i); + + return _dbus_type_writer_write_fixed_multi (writer, + node->klass->typecode, + &v_ARRAY_INT16, count); +} + +static dbus_bool_t +int16_read_multi (TestTypeNode *node, + DBusTypeReader *reader, + int seed, + int count) +{ + /* also used for uint16 */ + dbus_int16_t *values; + int n_elements; + int i; + + check_expected_type (reader, node->klass->typecode); + + _dbus_type_reader_read_fixed_multi (reader, + &values, + &n_elements); + + if (n_elements != count) + _dbus_warn ("got %d elements expected %d\n", n_elements, count); + _dbus_assert (n_elements == count); + + for (i = 0; i < count; i++) + _dbus_assert (((dbus_int16_t)_dbus_unpack_uint16 (reader->byte_order, + (const unsigned char*)values + (i * 2))) == + int16_from_seed (seed + i)); + + return TRUE; +} + + +#define SAMPLE_INT32 12345678 +#define SAMPLE_INT32_ALTERNATE 53781429 +static dbus_int32_t +int32_from_seed (int seed) +{ + /* Generate an integer value that's predictable from seed. We could + * just use seed itself, but that would only ever touch one byte of + * the int so would miss some kinds of bug. + */ + dbus_int32_t v; + + v = 42; /* just to quiet compiler afaik */ + switch (seed % 5) + { + case 0: + v = SAMPLE_INT32; + break; + case 1: + v = SAMPLE_INT32_ALTERNATE; + break; + case 2: + v = -1; + break; + case 3: + v = _DBUS_INT_MAX; + break; + case 4: + v = 1; + break; + } + + if (seed > 1) + v *= seed; /* wraps around eventually, which is fine */ + + return v; +} + +static dbus_bool_t +int32_write_value (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed) +{ + /* also used for uint32 */ + dbus_int32_t v; + + v = int32_from_seed (seed); + + return _dbus_type_writer_write_basic (writer, + node->klass->typecode, + &v); +} + +static dbus_bool_t +int32_read_value (TestTypeNode *node, + DBusTypeReader *reader, + int seed) +{ + /* also used for uint32 */ + dbus_int32_t v; + + check_expected_type (reader, node->klass->typecode); + + _dbus_type_reader_read_basic (reader, + (dbus_int32_t*) &v); + + _dbus_assert (v == int32_from_seed (seed)); + + return TRUE; +} + +static dbus_bool_t +int32_set_value (TestTypeNode *node, + DBusTypeReader *reader, + DBusTypeReader *realign_root, + int seed) +{ + /* also used for uint32 */ + dbus_int32_t v; + + v = int32_from_seed (seed); + + return _dbus_type_reader_set_basic (reader, + &v, + realign_root); +} + +static dbus_bool_t +int32_write_multi (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed, + int count) +{ + /* also used for uint32 */ + dbus_int32_t values[MAX_MULTI_COUNT]; + dbus_int32_t *v_ARRAY_INT32 = values; + int i; + + for (i = 0; i < count; ++i) + values[i] = int32_from_seed (seed + i); + + return _dbus_type_writer_write_fixed_multi (writer, + node->klass->typecode, + &v_ARRAY_INT32, count); +} + +static dbus_bool_t +int32_read_multi (TestTypeNode *node, + DBusTypeReader *reader, + int seed, + int count) +{ + /* also used for uint32 */ + dbus_int32_t *values; + int n_elements; + int i; + + check_expected_type (reader, node->klass->typecode); + + _dbus_type_reader_read_fixed_multi (reader, + &values, + &n_elements); + + if (n_elements != count) + _dbus_warn ("got %d elements expected %d\n", n_elements, count); + _dbus_assert (n_elements == count); + + for (i = 0; i < count; i++) + _dbus_assert (((int)_dbus_unpack_uint32 (reader->byte_order, + (const unsigned char*)values + (i * 4))) == + int32_from_seed (seed + i)); + + return TRUE; +} + +#ifdef DBUS_HAVE_INT64 +static dbus_int64_t +int64_from_seed (int seed) +{ + dbus_int32_t v32; + dbus_int64_t v; + + v32 = int32_from_seed (seed); + + v = - (dbus_int32_t) ~ v32; + v |= (((dbus_int64_t)v32) << 32); + + return v; +} +#endif + +static dbus_bool_t +int64_write_value (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed) +{ +#ifdef DBUS_HAVE_INT64 + /* also used for uint64 */ + dbus_int64_t v; + + v = int64_from_seed (seed); + + return _dbus_type_writer_write_basic (writer, + node->klass->typecode, + &v); +#else + return TRUE; +#endif +} + +static dbus_bool_t +int64_read_value (TestTypeNode *node, + DBusTypeReader *reader, + int seed) +{ +#ifdef DBUS_HAVE_INT64 + /* also used for uint64 */ + dbus_int64_t v; + + check_expected_type (reader, node->klass->typecode); + + _dbus_type_reader_read_basic (reader, + (dbus_int64_t*) &v); + + _dbus_assert (v == int64_from_seed (seed)); + + return TRUE; +#else + return TRUE; +#endif +} + +static dbus_bool_t +int64_set_value (TestTypeNode *node, + DBusTypeReader *reader, + DBusTypeReader *realign_root, + int seed) +{ +#ifdef DBUS_HAVE_INT64 + /* also used for uint64 */ + dbus_int64_t v; + + v = int64_from_seed (seed); + + return _dbus_type_reader_set_basic (reader, + &v, + realign_root); +#else + return TRUE; +#endif +} + +#define MAX_SAMPLE_STRING_LEN 10 +static void +string_from_seed (char *buf, + int len, + int seed) +{ + int i; + unsigned char v; + + _dbus_assert (len < MAX_SAMPLE_STRING_LEN); + + /* vary the length slightly, though we also have multiple string + * value types for this, varying it here tests the set_value code + */ + switch (seed % 3) + { + case 1: + len += 2; + break; + case 2: + len -= 2; + break; + } + if (len < 0) + len = 0; + + v = (unsigned char) ('A' + seed); + + i = 0; + while (i < len) + { + if (v < 'A' || v > 'z') + v = 'A'; + + buf[i] = v; + + v += 1; + ++i; + } + + buf[i] = '\0'; +} + +static dbus_bool_t +string_write_value (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed) +{ + char buf[MAX_SAMPLE_STRING_LEN + 1]=""; + const char *v_string = buf; + + + string_from_seed (buf, node->klass->subclass_detail, + seed); + + return _dbus_type_writer_write_basic (writer, + node->klass->typecode, + &v_string); +} + +static dbus_bool_t +string_read_value (TestTypeNode *node, + DBusTypeReader *reader, + int seed) +{ + const char *v; + char buf[MAX_SAMPLE_STRING_LEN + 1]; + v = buf; + + check_expected_type (reader, node->klass->typecode); + + _dbus_type_reader_read_basic (reader, + (const char **) &v); + + string_from_seed (buf, node->klass->subclass_detail, + seed); + + if (strcmp (buf, v) != 0) + { + _dbus_warn ("read string '%s' expected '%s'\n", + v, buf); + _dbus_assert_not_reached ("test failed"); + } + + return TRUE; +} + +static dbus_bool_t +string_set_value (TestTypeNode *node, + DBusTypeReader *reader, + DBusTypeReader *realign_root, + int seed) +{ + char buf[MAX_SAMPLE_STRING_LEN + 1]; + const char *v_string = buf; + + string_from_seed (buf, node->klass->subclass_detail, + seed); + +#if RECURSIVE_MARSHAL_WRITE_TRACE + { + const char *old; + _dbus_type_reader_read_basic (reader, &old); + _dbus_verbose ("SETTING new string '%s' len %d in place of '%s' len %d\n", + v_string, strlen (v_string), old, strlen (old)); + } +#endif + + return _dbus_type_reader_set_basic (reader, + &v_string, + realign_root); +} + +#define BOOL_FROM_SEED(seed) ((dbus_bool_t)((seed) % 2)) + +static dbus_bool_t +bool_write_value (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed) +{ + dbus_bool_t v; + + v = BOOL_FROM_SEED (seed); + + return _dbus_type_writer_write_basic (writer, + node->klass->typecode, + &v); +} + +static dbus_bool_t +bool_read_value (TestTypeNode *node, + DBusTypeReader *reader, + int seed) +{ + dbus_bool_t v; + + check_expected_type (reader, node->klass->typecode); + + _dbus_type_reader_read_basic (reader, + (unsigned char*) &v); + + _dbus_assert (v == BOOL_FROM_SEED (seed)); + + return TRUE; +} + +static dbus_bool_t +bool_set_value (TestTypeNode *node, + DBusTypeReader *reader, + DBusTypeReader *realign_root, + int seed) +{ + dbus_bool_t v; + + v = BOOL_FROM_SEED (seed); + + return _dbus_type_reader_set_basic (reader, + &v, + realign_root); +} + +#define BYTE_FROM_SEED(seed) ((unsigned char) int32_from_seed (seed)) + +static dbus_bool_t +byte_write_value (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed) +{ + unsigned char v; + + v = BYTE_FROM_SEED (seed); + + return _dbus_type_writer_write_basic (writer, + node->klass->typecode, + &v); +} + +static dbus_bool_t +byte_read_value (TestTypeNode *node, + DBusTypeReader *reader, + int seed) +{ + unsigned char v; + + check_expected_type (reader, node->klass->typecode); + + _dbus_type_reader_read_basic (reader, + (unsigned char*) &v); + + _dbus_assert (v == BYTE_FROM_SEED (seed)); + + return TRUE; +} + + +static dbus_bool_t +byte_set_value (TestTypeNode *node, + DBusTypeReader *reader, + DBusTypeReader *realign_root, + int seed) +{ + unsigned char v; + + v = BYTE_FROM_SEED (seed); + + return _dbus_type_reader_set_basic (reader, + &v, + realign_root); +} + +static double +double_from_seed (int seed) +{ + return SAMPLE_INT32 * (double) seed + 0.3; +} + +static dbus_bool_t +double_write_value (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed) +{ + double v; + + v = double_from_seed (seed); + + return _dbus_type_writer_write_basic (writer, + node->klass->typecode, + &v); +} + +static dbus_bool_t +double_read_value (TestTypeNode *node, + DBusTypeReader *reader, + int seed) +{ + double v; + double expected; + + check_expected_type (reader, node->klass->typecode); + + _dbus_type_reader_read_basic (reader, + (double*) &v); + + expected = double_from_seed (seed); + + if (!_DBUS_DOUBLES_BITWISE_EQUAL (v, expected)) + { +#ifdef DBUS_HAVE_INT64 + _dbus_warn ("Expected double %g got %g\n bits = 0x%llx vs.\n bits = 0x%llx)\n", + expected, v, + *(dbus_uint64_t*)(char*)&expected, + *(dbus_uint64_t*)(char*)&v); +#endif + _dbus_assert_not_reached ("test failed"); + } + + return TRUE; +} + +static dbus_bool_t +double_set_value (TestTypeNode *node, + DBusTypeReader *reader, + DBusTypeReader *realign_root, + int seed) +{ + double v; + + v = double_from_seed (seed); + + return _dbus_type_reader_set_basic (reader, + &v, + realign_root); +} + +#define MAX_SAMPLE_OBJECT_PATH_LEN 10 +static void +object_path_from_seed (char *buf, + int seed) +{ + int i; + unsigned char v; + int len; + + len = seed % 9; + _dbus_assert (len < MAX_SAMPLE_OBJECT_PATH_LEN); + + v = (unsigned char) ('A' + seed); + + if (len < 2) + { + buf[0] = '/'; + i = 1; + } + else + { + i = 0; + while (i + 1 < len) + { + if (v < 'A' || v > 'z') + v = 'A'; + + buf[i] = '/'; + ++i; + buf[i] = v; + ++i; + + v += 1; + } + } + + buf[i] = '\0'; +} + +static dbus_bool_t +object_path_write_value (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed) +{ + char buf[MAX_SAMPLE_OBJECT_PATH_LEN + 1]; + const char *v_string = buf; + + object_path_from_seed (buf, seed); + + return _dbus_type_writer_write_basic (writer, + node->klass->typecode, + &v_string); +} + +static dbus_bool_t +object_path_read_value (TestTypeNode *node, + DBusTypeReader *reader, + int seed) +{ + const char *v; + char buf[MAX_SAMPLE_OBJECT_PATH_LEN + 1]; + + check_expected_type (reader, node->klass->typecode); + + _dbus_type_reader_read_basic (reader, + (const char **) &v); + + object_path_from_seed (buf, seed); + + if (strcmp (buf, v) != 0) + { + _dbus_warn ("read object path '%s' expected '%s'\n", + v, buf); + _dbus_assert_not_reached ("test failed"); + } + + return TRUE; +} + +static dbus_bool_t +object_path_set_value (TestTypeNode *node, + DBusTypeReader *reader, + DBusTypeReader *realign_root, + int seed) +{ + char buf[MAX_SAMPLE_OBJECT_PATH_LEN + 1]; + const char *v_string = buf; + + object_path_from_seed (buf, seed); + + return _dbus_type_reader_set_basic (reader, + &v_string, + realign_root); +} + +#define MAX_SAMPLE_SIGNATURE_LEN 10 +static void +signature_from_seed (char *buf, + int seed) +{ + /* try to avoid ascending, descending, or alternating length to help find bugs */ + const char *sample_signatures[] = { + "asax" + "", + "asau(xxxx)", + "x", + "ai", + "a(ii)" + }; + + strcpy (buf, sample_signatures[seed % _DBUS_N_ELEMENTS(sample_signatures)]); +} + +static dbus_bool_t +signature_write_value (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed) +{ + char buf[MAX_SAMPLE_SIGNATURE_LEN + 1]; + const char *v_string = buf; + + signature_from_seed (buf, seed); + + return _dbus_type_writer_write_basic (writer, + node->klass->typecode, + &v_string); +} + +static dbus_bool_t +signature_read_value (TestTypeNode *node, + DBusTypeReader *reader, + int seed) +{ + const char *v; + char buf[MAX_SAMPLE_SIGNATURE_LEN + 1]; + + check_expected_type (reader, node->klass->typecode); + + _dbus_type_reader_read_basic (reader, + (const char **) &v); + + signature_from_seed (buf, seed); + + if (strcmp (buf, v) != 0) + { + _dbus_warn ("read signature value '%s' expected '%s'\n", + v, buf); + _dbus_assert_not_reached ("test failed"); + } + + return TRUE; +} + + +static dbus_bool_t +signature_set_value (TestTypeNode *node, + DBusTypeReader *reader, + DBusTypeReader *realign_root, + int seed) +{ + char buf[MAX_SAMPLE_SIGNATURE_LEN + 1]; + const char *v_string = buf; + + signature_from_seed (buf, seed); + + return _dbus_type_reader_set_basic (reader, + &v_string, + realign_root); +} + +static dbus_bool_t +struct_write_value (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed) +{ + TestTypeNodeContainer *container = (TestTypeNodeContainer*) node; + DataBlockState saved; + DBusTypeWriter sub; + int i; + int n_copies; + + n_copies = node->klass->subclass_detail; + + _dbus_assert (container->children != NULL); + + data_block_save (block, &saved); + + if (!_dbus_type_writer_recurse (writer, DBUS_TYPE_STRUCT, + NULL, 0, + &sub)) + return FALSE; + + i = 0; + while (i < n_copies) + { + DBusList *link; + + link = _dbus_list_get_first_link (&container->children); + while (link != NULL) + { + TestTypeNode *child = link->data; + DBusList *next = _dbus_list_get_next_link (&container->children, link); + + if (!node_write_value (child, block, &sub, seed + i)) + { + data_block_restore (block, &saved); + return FALSE; + } + + link = next; + } + + ++i; + } + + if (!_dbus_type_writer_unrecurse (writer, &sub)) + { + data_block_restore (block, &saved); + return FALSE; + } + + return TRUE; +} + +static dbus_bool_t +struct_read_or_set_value (TestTypeNode *node, + DBusTypeReader *reader, + DBusTypeReader *realign_root, + int seed) +{ + TestTypeNodeContainer *container = (TestTypeNodeContainer*) node; + DBusTypeReader sub; + int i; + int n_copies; + + n_copies = node->klass->subclass_detail; + + check_expected_type (reader, DBUS_TYPE_STRUCT); + + _dbus_type_reader_recurse (reader, &sub); + + i = 0; + while (i < n_copies) + { + DBusList *link; + + link = _dbus_list_get_first_link (&container->children); + while (link != NULL) + { + TestTypeNode *child = link->data; + DBusList *next = _dbus_list_get_next_link (&container->children, link); + + if (realign_root == NULL) + { + if (!node_read_value (child, &sub, seed + i)) + return FALSE; + } + else + { + if (!node_set_value (child, &sub, realign_root, seed + i)) + return FALSE; + } + + if (i == (n_copies - 1) && next == NULL) + NEXT_EXPECTING_FALSE (&sub); + else + NEXT_EXPECTING_TRUE (&sub); + + link = next; + } + + ++i; + } + + return TRUE; +} + +static dbus_bool_t +struct_read_value (TestTypeNode *node, + DBusTypeReader *reader, + int seed) +{ + return struct_read_or_set_value (node, reader, NULL, seed); +} + +static dbus_bool_t +struct_set_value (TestTypeNode *node, + DBusTypeReader *reader, + DBusTypeReader *realign_root, + int seed) +{ + return struct_read_or_set_value (node, reader, realign_root, seed); +} + +static dbus_bool_t +struct_build_signature (TestTypeNode *node, + DBusString *str) +{ + TestTypeNodeContainer *container = (TestTypeNodeContainer*) node; + int i; + int orig_len; + int n_copies; + + n_copies = node->klass->subclass_detail; + + orig_len = _dbus_string_get_length (str); + + if (!_dbus_string_append_byte (str, DBUS_STRUCT_BEGIN_CHAR)) + goto oom; + + i = 0; + while (i < n_copies) + { + DBusList *link; + + link = _dbus_list_get_first_link (&container->children); + while (link != NULL) + { + TestTypeNode *child = link->data; + DBusList *next = _dbus_list_get_next_link (&container->children, link); + + if (!node_build_signature (child, str)) + goto oom; + + link = next; + } + + ++i; + } + + if (!_dbus_string_append_byte (str, DBUS_STRUCT_END_CHAR)) + goto oom; + + return TRUE; + + oom: + _dbus_string_set_length (str, orig_len); + return FALSE; +} + +static dbus_bool_t +array_write_value (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed) +{ + TestTypeNodeContainer *container = (TestTypeNodeContainer*) node; + DataBlockState saved; + DBusTypeWriter sub; + DBusString element_signature; + int i; + int n_copies; + int element_type; + TestTypeNode *child; + + n_copies = node->klass->subclass_detail; + + _dbus_assert (container->children != NULL); + + data_block_save (block, &saved); + + if (!_dbus_string_init (&element_signature)) + return FALSE; + + child = _dbus_list_get_first (&container->children); + + if (!node_build_signature (child, + &element_signature)) + goto oom; + + element_type = _dbus_first_type_in_signature (&element_signature, 0); + + if (!_dbus_type_writer_recurse (writer, DBUS_TYPE_ARRAY, + &element_signature, 0, + &sub)) + goto oom; + + if (arrays_write_fixed_in_blocks && + dbus_type_is_fixed (element_type) && + child->klass->write_multi) + { + if (!node_write_multi (child, block, &sub, seed, n_copies)) + goto oom; + } + else + { + i = 0; + while (i < n_copies) + { + DBusList *link; + + link = _dbus_list_get_first_link (&container->children); + while (link != NULL) + { + TestTypeNode *child = link->data; + DBusList *next = _dbus_list_get_next_link (&container->children, link); + + if (!node_write_value (child, block, &sub, seed + i)) + goto oom; + + link = next; + } + + ++i; + } + } + + if (!_dbus_type_writer_unrecurse (writer, &sub)) + goto oom; + + _dbus_string_free (&element_signature); + return TRUE; + + oom: + data_block_restore (block, &saved); + _dbus_string_free (&element_signature); + return FALSE; +} + +static dbus_bool_t +array_read_or_set_value (TestTypeNode *node, + DBusTypeReader *reader, + DBusTypeReader *realign_root, + int seed) +{ + TestTypeNodeContainer *container = (TestTypeNodeContainer*) node; + DBusTypeReader sub; + int i; + int n_copies; + TestTypeNode *child; + + n_copies = node->klass->subclass_detail; + + check_expected_type (reader, DBUS_TYPE_ARRAY); + + child = _dbus_list_get_first (&container->children); + + if (n_copies > 0) + { + _dbus_type_reader_recurse (reader, &sub); + + if (realign_root == NULL && arrays_write_fixed_in_blocks && + dbus_type_is_fixed (_dbus_type_reader_get_element_type (reader)) && + child->klass->read_multi) + { + if (!node_read_multi (child, &sub, seed, n_copies)) + return FALSE; + } + else + { + i = 0; + while (i < n_copies) + { + DBusList *link; + + link = _dbus_list_get_first_link (&container->children); + while (link != NULL) + { + TestTypeNode *child = link->data; + DBusList *next = _dbus_list_get_next_link (&container->children, link); + + _dbus_assert (child->klass->typecode == + _dbus_type_reader_get_element_type (reader)); + + if (realign_root == NULL) + { + if (!node_read_value (child, &sub, seed + i)) + return FALSE; + } + else + { + if (!node_set_value (child, &sub, realign_root, seed + i)) + return FALSE; + } + + if (i == (n_copies - 1) && next == NULL) + NEXT_EXPECTING_FALSE (&sub); + else + NEXT_EXPECTING_TRUE (&sub); + + link = next; + } + + ++i; + } + } + } + + return TRUE; +} + +static dbus_bool_t +array_read_value (TestTypeNode *node, + DBusTypeReader *reader, + int seed) +{ + return array_read_or_set_value (node, reader, NULL, seed); +} + +static dbus_bool_t +array_set_value (TestTypeNode *node, + DBusTypeReader *reader, + DBusTypeReader *realign_root, + int seed) +{ + return array_read_or_set_value (node, reader, realign_root, seed); +} + +static dbus_bool_t +array_build_signature (TestTypeNode *node, + DBusString *str) +{ + TestTypeNodeContainer *container = (TestTypeNodeContainer*) node; + int orig_len; + + orig_len = _dbus_string_get_length (str); + + if (!_dbus_string_append_byte (str, DBUS_TYPE_ARRAY)) + goto oom; + + if (!node_build_signature (_dbus_list_get_first (&container->children), + str)) + goto oom; + + return TRUE; + + oom: + _dbus_string_set_length (str, orig_len); + return FALSE; +} + + /* 10 is random just to add another seed that we use in the suite */ +#define VARIANT_SEED 10 + +static dbus_bool_t +variant_write_value (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed) +{ + TestTypeNodeContainer *container = (TestTypeNodeContainer*) node; + DataBlockState saved; + DBusTypeWriter sub; + DBusString content_signature; + TestTypeNode *child; + + _dbus_assert (container->children != NULL); + _dbus_assert (_dbus_list_length_is_one (&container->children)); + + child = _dbus_list_get_first (&container->children); + + data_block_save (block, &saved); + + if (!_dbus_string_init (&content_signature)) + return FALSE; + + if (!node_build_signature (child, + &content_signature)) + goto oom; + + if (!_dbus_type_writer_recurse (writer, DBUS_TYPE_VARIANT, + &content_signature, 0, + &sub)) + goto oom; + + if (!node_write_value (child, block, &sub, seed + VARIANT_SEED)) + goto oom; + + if (!_dbus_type_writer_unrecurse (writer, &sub)) + goto oom; + + _dbus_string_free (&content_signature); + return TRUE; + + oom: + data_block_restore (block, &saved); + _dbus_string_free (&content_signature); + return FALSE; +} + +static dbus_bool_t +variant_read_or_set_value (TestTypeNode *node, + DBusTypeReader *reader, + DBusTypeReader *realign_root, + int seed) +{ + TestTypeNodeContainer *container = (TestTypeNodeContainer*) node; + DBusTypeReader sub; + TestTypeNode *child; + + _dbus_assert (container->children != NULL); + _dbus_assert (_dbus_list_length_is_one (&container->children)); + + child = _dbus_list_get_first (&container->children); + + check_expected_type (reader, DBUS_TYPE_VARIANT); + + _dbus_type_reader_recurse (reader, &sub); + + if (realign_root == NULL) + { + if (!node_read_value (child, &sub, seed + VARIANT_SEED)) + return FALSE; + } + else + { + if (!node_set_value (child, &sub, realign_root, seed + VARIANT_SEED)) + return FALSE; + } + + NEXT_EXPECTING_FALSE (&sub); + + return TRUE; +} + +static dbus_bool_t +variant_read_value (TestTypeNode *node, + DBusTypeReader *reader, + int seed) +{ + return variant_read_or_set_value (node, reader, NULL, seed); +} + +static dbus_bool_t +variant_set_value (TestTypeNode *node, + DBusTypeReader *reader, + DBusTypeReader *realign_root, + int seed) +{ + return variant_read_or_set_value (node, reader, realign_root, seed); +} + +static dbus_bool_t +dict_write_value (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed) +{ + TestTypeNodeContainer *container = (TestTypeNodeContainer*) node; + DataBlockState saved; + DBusTypeWriter sub; + DBusString entry_value_signature; + DBusString dict_entry_signature; + int i; + int n_entries; + int entry_value_type; + TestTypeNode *child; + + n_entries = node->klass->subclass_detail; + + _dbus_assert (container->children != NULL); + + data_block_save (block, &saved); + + if (!_dbus_string_init (&entry_value_signature)) + return FALSE; + + if (!_dbus_string_init (&dict_entry_signature)) + { + _dbus_string_free (&entry_value_signature); + return FALSE; + } + + child = _dbus_list_get_first (&container->children); + + if (!node_build_signature (child, + &entry_value_signature)) + goto oom; + + if (!_dbus_string_append (&dict_entry_signature, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_INT32_AS_STRING)) + goto oom; + + if (!_dbus_string_copy (&entry_value_signature, 0, + &dict_entry_signature, + _dbus_string_get_length (&dict_entry_signature))) + goto oom; + + if (!_dbus_string_append_byte (&dict_entry_signature, + DBUS_DICT_ENTRY_END_CHAR)) + goto oom; + + entry_value_type = _dbus_first_type_in_signature (&entry_value_signature, 0); + + if (!_dbus_type_writer_recurse (writer, DBUS_TYPE_ARRAY, + &dict_entry_signature, 0, + &sub)) + goto oom; + + i = 0; + while (i < n_entries) + { + DBusTypeWriter entry_sub; + dbus_int32_t key; + + if (!_dbus_type_writer_recurse (&sub, DBUS_TYPE_DICT_ENTRY, + NULL, 0, + &entry_sub)) + goto oom; + + key = int32_from_seed (seed + i); + + if (!_dbus_type_writer_write_basic (&entry_sub, + DBUS_TYPE_INT32, + &key)) + goto oom; + + if (!node_write_value (child, block, &entry_sub, seed + i)) + goto oom; + + if (!_dbus_type_writer_unrecurse (&sub, &entry_sub)) + goto oom; + + ++i; + } + + if (!_dbus_type_writer_unrecurse (writer, &sub)) + goto oom; + + _dbus_string_free (&entry_value_signature); + _dbus_string_free (&dict_entry_signature); + return TRUE; + + oom: + data_block_restore (block, &saved); + _dbus_string_free (&entry_value_signature); + _dbus_string_free (&dict_entry_signature); + return FALSE; +} + +static dbus_bool_t +dict_read_or_set_value (TestTypeNode *node, + DBusTypeReader *reader, + DBusTypeReader *realign_root, + int seed) +{ + TestTypeNodeContainer *container = (TestTypeNodeContainer*) node; + DBusTypeReader sub; + int i; + int n_entries; + TestTypeNode *child; + + n_entries = node->klass->subclass_detail; + + check_expected_type (reader, DBUS_TYPE_ARRAY); + + child = _dbus_list_get_first (&container->children); + + if (n_entries > 0) + { + _dbus_type_reader_recurse (reader, &sub); + + check_expected_type (&sub, DBUS_TYPE_DICT_ENTRY); + + i = 0; + while (i < n_entries) + { + DBusTypeReader entry_sub; + + check_expected_type (&sub, DBUS_TYPE_DICT_ENTRY); + + _dbus_type_reader_recurse (&sub, &entry_sub); + + if (realign_root == NULL) + { + dbus_int32_t v; + + check_expected_type (&entry_sub, DBUS_TYPE_INT32); + + _dbus_type_reader_read_basic (&entry_sub, + (dbus_int32_t*) &v); + + _dbus_assert (v == int32_from_seed (seed + i)); + + NEXT_EXPECTING_TRUE (&entry_sub); + + if (!node_read_value (child, &entry_sub, seed + i)) + return FALSE; + + NEXT_EXPECTING_FALSE (&entry_sub); + } + else + { + dbus_int32_t v; + + v = int32_from_seed (seed + i); + + if (!_dbus_type_reader_set_basic (&entry_sub, + &v, + realign_root)) + return FALSE; + + NEXT_EXPECTING_TRUE (&entry_sub); + + if (!node_set_value (child, &entry_sub, realign_root, seed + i)) + return FALSE; + + NEXT_EXPECTING_FALSE (&entry_sub); + } + + if (i == (n_entries - 1)) + NEXT_EXPECTING_FALSE (&sub); + else + NEXT_EXPECTING_TRUE (&sub); + + ++i; + } + } + + return TRUE; +} + +static dbus_bool_t +dict_read_value (TestTypeNode *node, + DBusTypeReader *reader, + int seed) +{ + return dict_read_or_set_value (node, reader, NULL, seed); +} + +static dbus_bool_t +dict_set_value (TestTypeNode *node, + DBusTypeReader *reader, + DBusTypeReader *realign_root, + int seed) +{ + return dict_read_or_set_value (node, reader, realign_root, seed); +} + +static dbus_bool_t +dict_build_signature (TestTypeNode *node, + DBusString *str) +{ + TestTypeNodeContainer *container = (TestTypeNodeContainer*) node; + int orig_len; + + orig_len = _dbus_string_get_length (str); + + if (!_dbus_string_append_byte (str, DBUS_TYPE_ARRAY)) + goto oom; + + if (!_dbus_string_append (str, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_INT32_AS_STRING)) + goto oom; + + if (!node_build_signature (_dbus_list_get_first (&container->children), + str)) + goto oom; + + if (!_dbus_string_append_byte (str, DBUS_DICT_ENTRY_END_CHAR)) + goto oom; + + return TRUE; + + oom: + _dbus_string_set_length (str, orig_len); + return FALSE; +} + +static void +container_destroy (TestTypeNode *node) +{ + TestTypeNodeContainer *container = (TestTypeNodeContainer*) node; + DBusList *link; + + link = _dbus_list_get_first_link (&container->children); + while (link != NULL) + { + TestTypeNode *child = link->data; + DBusList *next = _dbus_list_get_next_link (&container->children, link); + + node_destroy (child); + + _dbus_list_free_link (link); + + link = next; + } +} + +#endif /* !DOXYGEN_SHOULD_SKIP_THIS */ + +#endif /* DBUS_BUILD_TESTS */ diff --git a/src/dbus/dbus-marshal-recursive.c b/src/dbus/dbus-marshal-recursive.c new file mode 100644 index 0000000..76ee344 --- /dev/null +++ b/src/dbus/dbus-marshal-recursive.c @@ -0,0 +1,2739 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-marshal-recursive.c Marshalling routines for recursive types + * + * Copyright (C) 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-marshal-recursive.h" +#include "dbus-marshal-basic.h" +#include "dbus-signature.h" +#include "dbus-internals.h" + +/** + * @addtogroup DBusMarshal + * @{ + */ + +/** turn this on to get deluged in TypeReader verbose spam */ +#define RECURSIVE_MARSHAL_READ_TRACE 0 + +/** turn this on to get deluged in TypeWriter verbose spam */ +#define RECURSIVE_MARSHAL_WRITE_TRACE 0 + +static void +free_fixups (DBusList **fixups) +{ + DBusList *link; + + link = _dbus_list_get_first_link (fixups); + while (link != NULL) + { + DBusList *next; + + next = _dbus_list_get_next_link (fixups, link); + + dbus_free (link->data); + _dbus_list_free_link (link); + + link = next; + } + + *fixups = NULL; +} + +static void +apply_and_free_fixups (DBusList **fixups, + DBusTypeReader *reader) +{ + DBusList *link; + +#if RECURSIVE_MARSHAL_WRITE_TRACE + if (*fixups) + _dbus_verbose (" %d FIXUPS to apply\n", + _dbus_list_get_length (fixups)); +#endif + + link = _dbus_list_get_first_link (fixups); + while (link != NULL) + { + DBusList *next; + + next = _dbus_list_get_next_link (fixups, link); + + if (reader) + { + DBusArrayLenFixup *f; + + f = link->data; + +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose (" applying FIXUP to reader %p at pos %d new_len = %d old len %d\n", + reader, f->len_pos_in_reader, f->new_len, + _dbus_marshal_read_uint32 (reader->value_str, + f->len_pos_in_reader, + reader->byte_order, NULL)); +#endif + + _dbus_marshal_set_uint32 ((DBusString*) reader->value_str, + f->len_pos_in_reader, + f->new_len, + reader->byte_order); + } + + dbus_free (link->data); + _dbus_list_free_link (link); + + link = next; + } + + *fixups = NULL; +} + +/** + * Virtual table for a type reader. + */ +struct DBusTypeReaderClass +{ + const char *name; /**< name for debugging */ + int id; /**< index in all_reader_classes */ + dbus_bool_t types_only; /**< only iterates over types, not values */ + void (* recurse) (DBusTypeReader *sub, + DBusTypeReader *parent); /**< recurse with this reader as sub */ + dbus_bool_t (* check_finished) (const DBusTypeReader *reader); /**< check whether reader is at the end */ + void (* next) (DBusTypeReader *reader, + int current_type); /**< go to the next value */ +}; + +static int +element_type_get_alignment (const DBusString *str, + int pos) +{ + return _dbus_type_get_alignment (_dbus_first_type_in_signature (str, pos)); +} + +static void +reader_init (DBusTypeReader *reader, + int byte_order, + const DBusString *type_str, + int type_pos, + const DBusString *value_str, + int value_pos) +{ + reader->byte_order = byte_order; + reader->finished = FALSE; + reader->type_str = type_str; + reader->type_pos = type_pos; + reader->value_str = value_str; + reader->value_pos = value_pos; +} + +static void +base_reader_recurse (DBusTypeReader *sub, + DBusTypeReader *parent) +{ + /* point subreader at the same place as parent */ + reader_init (sub, + parent->byte_order, + parent->type_str, + parent->type_pos, + parent->value_str, + parent->value_pos); +} + +static void +struct_or_dict_entry_types_only_reader_recurse (DBusTypeReader *sub, + DBusTypeReader *parent) +{ + base_reader_recurse (sub, parent); + + _dbus_assert (_dbus_string_get_byte (sub->type_str, + sub->type_pos) == DBUS_STRUCT_BEGIN_CHAR || + _dbus_string_get_byte (sub->type_str, + sub->type_pos) == DBUS_DICT_ENTRY_BEGIN_CHAR); + + sub->type_pos += 1; +} + +static void +struct_or_dict_entry_reader_recurse (DBusTypeReader *sub, + DBusTypeReader *parent) +{ + struct_or_dict_entry_types_only_reader_recurse (sub, parent); + + /* struct and dict entry have 8 byte alignment */ + sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, 8); +} + +static void +array_types_only_reader_recurse (DBusTypeReader *sub, + DBusTypeReader *parent) +{ + base_reader_recurse (sub, parent); + + /* point type_pos at the array element type */ + sub->type_pos += 1; + + /* Init with values likely to crash things if misused */ + sub->u.array.start_pos = _DBUS_INT_MAX; + sub->array_len_offset = 7; +} + +/** compute position of array length given array_len_offset, which is + the offset back from start_pos to end of the len */ +#define ARRAY_READER_LEN_POS(reader) \ + ((reader)->u.array.start_pos - ((int)(reader)->array_len_offset) - 4) + +static int +array_reader_get_array_len (const DBusTypeReader *reader) +{ + dbus_uint32_t array_len; + int len_pos; + + len_pos = ARRAY_READER_LEN_POS (reader); + + _dbus_assert (_DBUS_ALIGN_VALUE (len_pos, 4) == (unsigned) len_pos); + array_len = _dbus_unpack_uint32 (reader->byte_order, + _dbus_string_get_const_data_len (reader->value_str, len_pos, 4)); + +#if RECURSIVE_MARSHAL_READ_TRACE + _dbus_verbose (" reader %p len_pos %d array len %u len_offset %d\n", + reader, len_pos, array_len, reader->array_len_offset); +#endif + + _dbus_assert (reader->u.array.start_pos - len_pos - 4 < 8); + + return array_len; +} + +static void +array_reader_recurse (DBusTypeReader *sub, + DBusTypeReader *parent) +{ + int alignment; + int len_pos; + + array_types_only_reader_recurse (sub, parent); + + sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, 4); + + len_pos = sub->value_pos; + + sub->value_pos += 4; /* for the length */ + + alignment = element_type_get_alignment (sub->type_str, + sub->type_pos); + + sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, alignment); + + sub->u.array.start_pos = sub->value_pos; + _dbus_assert ((sub->u.array.start_pos - (len_pos + 4)) < 8); /* only 3 bits in array_len_offset */ + sub->array_len_offset = sub->u.array.start_pos - (len_pos + 4); + +#if RECURSIVE_MARSHAL_READ_TRACE + _dbus_verbose (" type reader %p array start = %d len_offset = %d array len = %d array element type = %s\n", + sub, + sub->u.array.start_pos, + sub->array_len_offset, + array_reader_get_array_len (sub), + _dbus_type_to_string (_dbus_first_type_in_signature (sub->type_str, + sub->type_pos))); +#endif +} + +static void +variant_reader_recurse (DBusTypeReader *sub, + DBusTypeReader *parent) +{ + int sig_len; + int contained_alignment; + + base_reader_recurse (sub, parent); + + /* Variant is 1 byte sig length (without nul), signature with nul, + * padding to 8-boundary, then values + */ + + sig_len = _dbus_string_get_byte (sub->value_str, sub->value_pos); + + sub->type_str = sub->value_str; + sub->type_pos = sub->value_pos + 1; + + sub->value_pos = sub->type_pos + sig_len + 1; + + contained_alignment = _dbus_type_get_alignment (_dbus_first_type_in_signature (sub->type_str, + sub->type_pos)); + + sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, contained_alignment); + +#if RECURSIVE_MARSHAL_READ_TRACE + _dbus_verbose (" type reader %p variant containing '%s'\n", + sub, + _dbus_string_get_const_data_len (sub->type_str, + sub->type_pos, 0)); +#endif +} + +static dbus_bool_t +array_reader_check_finished (const DBusTypeReader *reader) +{ + int end_pos; + + /* return the array element type if elements remain, and + * TYPE_INVALID otherwise + */ + + end_pos = reader->u.array.start_pos + array_reader_get_array_len (reader); + + _dbus_assert (reader->value_pos <= end_pos); + _dbus_assert (reader->value_pos >= reader->u.array.start_pos); + + return reader->value_pos == end_pos; +} + +static void +skip_one_complete_type (const DBusString *type_str, + int *type_pos) +{ + _dbus_type_signature_next (_dbus_string_get_const_data (type_str), + type_pos); +} + +/** + * Skips to the next "complete" type inside a type signature. + * The signature is read starting at type_pos, and the next + * type position is stored in the same variable. + * + * @param type_str a type signature (must be valid) + * @param type_pos an integer position in the type signature (in and out) + */ +void +_dbus_type_signature_next (const char *type_str, + int *type_pos) +{ + const unsigned char *p; + const unsigned char *start; + + _dbus_assert (type_str != NULL); + _dbus_assert (type_pos != NULL); + + start = type_str; + p = start + *type_pos; + + _dbus_assert (*p != DBUS_STRUCT_END_CHAR); + _dbus_assert (*p != DBUS_DICT_ENTRY_END_CHAR); + + while (*p == DBUS_TYPE_ARRAY) + ++p; + + _dbus_assert (*p != DBUS_STRUCT_END_CHAR); + _dbus_assert (*p != DBUS_DICT_ENTRY_END_CHAR); + + if (*p == DBUS_STRUCT_BEGIN_CHAR) + { + int depth; + + depth = 1; + + while (TRUE) + { + _dbus_assert (*p != DBUS_TYPE_INVALID); + + ++p; + + _dbus_assert (*p != DBUS_TYPE_INVALID); + + if (*p == DBUS_STRUCT_BEGIN_CHAR) + depth += 1; + else if (*p == DBUS_STRUCT_END_CHAR) + { + depth -= 1; + if (depth == 0) + { + ++p; + break; + } + } + } + } + else if (*p == DBUS_DICT_ENTRY_BEGIN_CHAR) + { + int depth; + + depth = 1; + + while (TRUE) + { + _dbus_assert (*p != DBUS_TYPE_INVALID); + + ++p; + + _dbus_assert (*p != DBUS_TYPE_INVALID); + + if (*p == DBUS_DICT_ENTRY_BEGIN_CHAR) + depth += 1; + else if (*p == DBUS_DICT_ENTRY_END_CHAR) + { + depth -= 1; + if (depth == 0) + { + ++p; + break; + } + } + } + } + else + { + ++p; + } + + *type_pos = (int) (p - start); +} + +static int +find_len_of_complete_type (const DBusString *type_str, + int type_pos) +{ + int end; + + end = type_pos; + + skip_one_complete_type (type_str, &end); + + return end - type_pos; +} + +static void +base_reader_next (DBusTypeReader *reader, + int current_type) +{ + switch (current_type) + { + case DBUS_TYPE_DICT_ENTRY: + case DBUS_TYPE_STRUCT: + case DBUS_TYPE_VARIANT: + /* Scan forward over the entire container contents */ + { + DBusTypeReader sub; + + if (reader->klass->types_only && current_type == DBUS_TYPE_VARIANT) + ; + else + { + /* Recurse into the struct or variant */ + _dbus_type_reader_recurse (reader, &sub); + + /* Skip everything in this subreader */ + while (_dbus_type_reader_next (&sub)) + { + /* nothing */; + } + } + if (!reader->klass->types_only) + reader->value_pos = sub.value_pos; + + /* Now we are at the end of this container; for variants, the + * subreader's type_pos is totally inapplicable (it's in the + * value string) but we know that we increment by one past the + * DBUS_TYPE_VARIANT + */ + if (current_type == DBUS_TYPE_VARIANT) + reader->type_pos += 1; + else + reader->type_pos = sub.type_pos; + } + break; + + case DBUS_TYPE_ARRAY: + { + if (!reader->klass->types_only) + _dbus_marshal_skip_array (reader->value_str, + _dbus_first_type_in_signature (reader->type_str, + reader->type_pos + 1), + reader->byte_order, + &reader->value_pos); + + skip_one_complete_type (reader->type_str, &reader->type_pos); + } + break; + + default: + if (!reader->klass->types_only) + _dbus_marshal_skip_basic (reader->value_str, + current_type, reader->byte_order, + &reader->value_pos); + + reader->type_pos += 1; + break; + } +} + +static void +struct_reader_next (DBusTypeReader *reader, + int current_type) +{ + int t; + + base_reader_next (reader, current_type); + + /* for STRUCT containers we return FALSE at the end of the struct, + * for INVALID we return FALSE at the end of the signature. + * In both cases we arrange for get_current_type() to return INVALID + * which is defined to happen iff we're at the end (no more next()) + */ + t = _dbus_string_get_byte (reader->type_str, reader->type_pos); + if (t == DBUS_STRUCT_END_CHAR) + { + reader->type_pos += 1; + reader->finished = TRUE; + } +} + +static void +dict_entry_reader_next (DBusTypeReader *reader, + int current_type) +{ + int t; + + base_reader_next (reader, current_type); + + /* for STRUCT containers we return FALSE at the end of the struct, + * for INVALID we return FALSE at the end of the signature. + * In both cases we arrange for get_current_type() to return INVALID + * which is defined to happen iff we're at the end (no more next()) + */ + t = _dbus_string_get_byte (reader->type_str, reader->type_pos); + if (t == DBUS_DICT_ENTRY_END_CHAR) + { + reader->type_pos += 1; + reader->finished = TRUE; + } +} + +static void +array_types_only_reader_next (DBusTypeReader *reader, + int current_type) +{ + /* We have one "element" to be iterated over + * in each array, which is its element type. + * So the finished flag indicates whether we've + * iterated over it yet or not. + */ + reader->finished = TRUE; +} + +static void +array_reader_next (DBusTypeReader *reader, + int current_type) +{ + /* Skip one array element */ + int end_pos; + + end_pos = reader->u.array.start_pos + array_reader_get_array_len (reader); + +#if RECURSIVE_MARSHAL_READ_TRACE + _dbus_verbose (" reader %p array next START start_pos = %d end_pos = %d value_pos = %d current_type = %s\n", + reader, + reader->u.array.start_pos, + end_pos, reader->value_pos, + _dbus_type_to_string (current_type)); +#endif + + _dbus_assert (reader->value_pos < end_pos); + _dbus_assert (reader->value_pos >= reader->u.array.start_pos); + + switch (_dbus_first_type_in_signature (reader->type_str, + reader->type_pos)) + { + case DBUS_TYPE_DICT_ENTRY: + case DBUS_TYPE_STRUCT: + case DBUS_TYPE_VARIANT: + { + DBusTypeReader sub; + + /* Recurse into the struct or variant */ + _dbus_type_reader_recurse (reader, &sub); + + /* Skip everything in this element */ + while (_dbus_type_reader_next (&sub)) + { + /* nothing */; + } + + /* Now we are at the end of this element */ + reader->value_pos = sub.value_pos; + } + break; + + case DBUS_TYPE_ARRAY: + { + _dbus_marshal_skip_array (reader->value_str, + _dbus_first_type_in_signature (reader->type_str, + reader->type_pos + 1), + reader->byte_order, + &reader->value_pos); + } + break; + + default: + { + _dbus_marshal_skip_basic (reader->value_str, + current_type, reader->byte_order, + &reader->value_pos); + } + break; + } + +#if RECURSIVE_MARSHAL_READ_TRACE + _dbus_verbose (" reader %p array next END start_pos = %d end_pos = %d value_pos = %d current_type = %s\n", + reader, + reader->u.array.start_pos, + end_pos, reader->value_pos, + _dbus_type_to_string (current_type)); +#endif + + _dbus_assert (reader->value_pos <= end_pos); + + if (reader->value_pos == end_pos) + { + skip_one_complete_type (reader->type_str, + &reader->type_pos); + } +} + +static const DBusTypeReaderClass body_reader_class = { + "body", 0, + FALSE, + NULL, /* body is always toplevel, so doesn't get recursed into */ + NULL, + base_reader_next +}; + +static const DBusTypeReaderClass body_types_only_reader_class = { + "body types", 1, + TRUE, + NULL, /* body is always toplevel, so doesn't get recursed into */ + NULL, + base_reader_next +}; + +static const DBusTypeReaderClass struct_reader_class = { + "struct", 2, + FALSE, + struct_or_dict_entry_reader_recurse, + NULL, + struct_reader_next +}; + +static const DBusTypeReaderClass struct_types_only_reader_class = { + "struct types", 3, + TRUE, + struct_or_dict_entry_types_only_reader_recurse, + NULL, + struct_reader_next +}; + +static const DBusTypeReaderClass dict_entry_reader_class = { + "dict_entry", 4, + FALSE, + struct_or_dict_entry_reader_recurse, + NULL, + dict_entry_reader_next +}; + +static const DBusTypeReaderClass dict_entry_types_only_reader_class = { + "dict_entry types", 5, + TRUE, + struct_or_dict_entry_types_only_reader_recurse, + NULL, + dict_entry_reader_next +}; + +static const DBusTypeReaderClass array_reader_class = { + "array", 6, + FALSE, + array_reader_recurse, + array_reader_check_finished, + array_reader_next +}; + +static const DBusTypeReaderClass array_types_only_reader_class = { + "array types", 7, + TRUE, + array_types_only_reader_recurse, + NULL, + array_types_only_reader_next +}; + +static const DBusTypeReaderClass variant_reader_class = { + "variant", 8, + FALSE, + variant_reader_recurse, + NULL, + base_reader_next +}; + +#ifndef DBUS_DISABLE_ASSERT +static const DBusTypeReaderClass * const +all_reader_classes[] = { + &body_reader_class, + &body_types_only_reader_class, + &struct_reader_class, + &struct_types_only_reader_class, + &dict_entry_reader_class, + &dict_entry_types_only_reader_class, + &array_reader_class, + &array_types_only_reader_class, + &variant_reader_class +}; +#endif + +/** + * Initializes a type reader. + * + * @param reader the reader + * @param byte_order the byte order of the block to read + * @param type_str the signature of the block to read + * @param type_pos location of signature + * @param value_str the string containing values block + * @param value_pos start of values block + */ +void +_dbus_type_reader_init (DBusTypeReader *reader, + int byte_order, + const DBusString *type_str, + int type_pos, + const DBusString *value_str, + int value_pos) +{ + reader->klass = &body_reader_class; + + reader_init (reader, byte_order, type_str, type_pos, + value_str, value_pos); + +#if RECURSIVE_MARSHAL_READ_TRACE + _dbus_verbose (" type reader %p init type_pos = %d value_pos = %d remaining sig '%s'\n", + reader, reader->type_pos, reader->value_pos, + _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0)); +#endif +} + +/** + * Like _dbus_type_reader_init() but the iteration is over the + * signature, not over values. + * + * @param reader the reader + * @param type_str the signature string + * @param type_pos location in the signature string + */ +void +_dbus_type_reader_init_types_only (DBusTypeReader *reader, + const DBusString *type_str, + int type_pos) +{ + reader->klass = &body_types_only_reader_class; + + reader_init (reader, DBUS_COMPILER_BYTE_ORDER /* irrelevant */, + type_str, type_pos, NULL, _DBUS_INT_MAX /* crashes if we screw up */); + +#if RECURSIVE_MARSHAL_READ_TRACE + _dbus_verbose (" type reader %p init types only type_pos = %d remaining sig '%s'\n", + reader, reader->type_pos, + _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0)); +#endif +} + +/** + * Gets the type of the value the reader is currently pointing to; + * or for a types-only reader gets the type it's currently pointing to. + * If the reader is at the end of a block or end of a container such + * as an array, returns #DBUS_TYPE_INVALID. + * + * @param reader the reader + */ +int +_dbus_type_reader_get_current_type (const DBusTypeReader *reader) +{ + int t; + + if (reader->finished || + (reader->klass->check_finished && + (* reader->klass->check_finished) (reader))) + t = DBUS_TYPE_INVALID; + else + t = _dbus_first_type_in_signature (reader->type_str, + reader->type_pos); + + _dbus_assert (t != DBUS_STRUCT_END_CHAR); + _dbus_assert (t != DBUS_STRUCT_BEGIN_CHAR); + _dbus_assert (t != DBUS_DICT_ENTRY_END_CHAR); + _dbus_assert (t != DBUS_DICT_ENTRY_BEGIN_CHAR); + +#if 0 + _dbus_verbose (" type reader %p current type_pos = %d type = %s\n", + reader, reader->type_pos, + _dbus_type_to_string (t)); +#endif + + return t; +} + +/** + * Gets the type of an element of the array the reader is currently + * pointing to. It's an error to call this if + * _dbus_type_reader_get_current_type() doesn't return #DBUS_TYPE_ARRAY + * for this reader. + * + * @param reader the reader + */ +int +_dbus_type_reader_get_element_type (const DBusTypeReader *reader) +{ + int element_type; + + _dbus_assert (_dbus_type_reader_get_current_type (reader) == DBUS_TYPE_ARRAY); + + element_type = _dbus_first_type_in_signature (reader->type_str, + reader->type_pos + 1); + + return element_type; +} + +/** + * Gets the current position in the value block + * @param reader the reader + */ +int +_dbus_type_reader_get_value_pos (const DBusTypeReader *reader) +{ + return reader->value_pos; +} + +/** + * Get the address of the marshaled value in the data being read. The + * address may not be aligned; you have to align it to the type of the + * value you want to read. Most of the demarshal routines do this for + * you. + * + * @param reader the reader + * @param value_location the address of the marshaled value + */ +void +_dbus_type_reader_read_raw (const DBusTypeReader *reader, + const unsigned char **value_location) +{ + _dbus_assert (!reader->klass->types_only); + + *value_location = _dbus_string_get_const_data_len (reader->value_str, + reader->value_pos, + 0); +} + +/** + * Reads a basic-typed value, as with _dbus_marshal_read_basic(). + * + * @param reader the reader + * @param value the address of the value + */ +void +_dbus_type_reader_read_basic (const DBusTypeReader *reader, + void *value) +{ + int t; + + _dbus_assert (!reader->klass->types_only); + + t = _dbus_type_reader_get_current_type (reader); + + _dbus_marshal_read_basic (reader->value_str, + reader->value_pos, + t, value, + reader->byte_order, + NULL); + + +#if RECURSIVE_MARSHAL_READ_TRACE + _dbus_verbose (" type reader %p read basic type_pos = %d value_pos = %d remaining sig '%s'\n", + reader, reader->type_pos, reader->value_pos, + _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0)); +#endif +} + +/** + * Returns the number of bytes in the array. + * + * @param reader the reader to read from + * @returns the number of bytes in the array + */ +int +_dbus_type_reader_get_array_length (const DBusTypeReader *reader) +{ + _dbus_assert (!reader->klass->types_only); + _dbus_assert (reader->klass == &array_reader_class); + + return array_reader_get_array_len (reader); +} + +/** + * Reads a block of fixed-length basic values, from the current point + * in an array to the end of the array. Does not work for arrays of + * string or container types. + * + * This function returns the array in-place; it does not make a copy, + * and it does not swap the bytes. + * + * If you ask for #DBUS_TYPE_DOUBLE you will get a "const double*" back + * and the "value" argument should be a "const double**" and so on. + * + * @param reader the reader to read from + * @param value place to return the array values + * @param n_elements place to return number of array elements + */ +void +_dbus_type_reader_read_fixed_multi (const DBusTypeReader *reader, + void *value, + int *n_elements) +{ + int element_type; + int end_pos; + int remaining_len; + int alignment; + int total_len; + + _dbus_assert (!reader->klass->types_only); + _dbus_assert (reader->klass == &array_reader_class); + + element_type = _dbus_first_type_in_signature (reader->type_str, + reader->type_pos); + + _dbus_assert (element_type != DBUS_TYPE_INVALID); /* why we don't use get_current_type() */ + _dbus_assert (dbus_type_is_fixed (element_type)); + + alignment = _dbus_type_get_alignment (element_type); + + _dbus_assert (reader->value_pos >= reader->u.array.start_pos); + + total_len = array_reader_get_array_len (reader); + end_pos = reader->u.array.start_pos + total_len; + remaining_len = end_pos - reader->value_pos; + +#if RECURSIVE_MARSHAL_READ_TRACE + _dbus_verbose ("end_pos %d total_len %d remaining_len %d value_pos %d\n", + end_pos, total_len, remaining_len, reader->value_pos); +#endif + + _dbus_assert (remaining_len <= total_len); + + if (remaining_len == 0) + *(const DBusBasicValue**) value = NULL; + else + *(const DBusBasicValue**) value = + (void*) _dbus_string_get_const_data_len (reader->value_str, + reader->value_pos, + remaining_len); + + *n_elements = remaining_len / alignment; + _dbus_assert ((remaining_len % alignment) == 0); + +#if RECURSIVE_MARSHAL_READ_TRACE + _dbus_verbose (" type reader %p read fixed array type_pos = %d value_pos = %d remaining sig '%s'\n", + reader, reader->type_pos, reader->value_pos, + _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0)); +#endif +} + +/** + * Initialize a new reader pointing to the first type and + * corresponding value that's a child of the current container. It's + * an error to call this if the current type is a non-container. + * + * Note that DBusTypeReader traverses values, not types. So if you + * have an empty array of array of int, you can't recurse into it. You + * can only recurse into each element. + * + * @param reader the reader + * @param sub a reader to init pointing to the first child + */ +void +_dbus_type_reader_recurse (DBusTypeReader *reader, + DBusTypeReader *sub) +{ + int t; + + t = _dbus_first_type_in_signature (reader->type_str, reader->type_pos); + + switch (t) + { + case DBUS_TYPE_STRUCT: + if (reader->klass->types_only) + sub->klass = &struct_types_only_reader_class; + else + sub->klass = &struct_reader_class; + break; + case DBUS_TYPE_DICT_ENTRY: + if (reader->klass->types_only) + sub->klass = &dict_entry_types_only_reader_class; + else + sub->klass = &dict_entry_reader_class; + break; + case DBUS_TYPE_ARRAY: + if (reader->klass->types_only) + sub->klass = &array_types_only_reader_class; + else + sub->klass = &array_reader_class; + break; + case DBUS_TYPE_VARIANT: + if (reader->klass->types_only) + _dbus_assert_not_reached ("can't recurse into variant typecode"); + else + sub->klass = &variant_reader_class; + break; + default: + _dbus_verbose ("recursing into type %s\n", _dbus_type_to_string (t)); +#ifndef DBUS_DISABLE_CHECKS + if (t == DBUS_TYPE_INVALID) + _dbus_warn_check_failed ("You can't recurse into an empty array or off the end of a message body\n"); +#endif /* DBUS_DISABLE_CHECKS */ + + _dbus_assert_not_reached ("don't yet handle recursing into this type"); + } + + _dbus_assert (sub->klass == all_reader_classes[sub->klass->id]); + + (* sub->klass->recurse) (sub, reader); + +#if RECURSIVE_MARSHAL_READ_TRACE + _dbus_verbose (" type reader %p RECURSED type_pos = %d value_pos = %d remaining sig '%s'\n", + sub, sub->type_pos, sub->value_pos, + _dbus_string_get_const_data_len (sub->type_str, sub->type_pos, 0)); +#endif +} + +/** + * Skip to the next value on this "level". e.g. the next field in a + * struct, the next value in an array. Returns FALSE at the end of the + * current container. + * + * @param reader the reader + * @returns FALSE if nothing more to read at or below this level + */ +dbus_bool_t +_dbus_type_reader_next (DBusTypeReader *reader) +{ + int t; + + t = _dbus_type_reader_get_current_type (reader); + +#if RECURSIVE_MARSHAL_READ_TRACE + _dbus_verbose (" type reader %p START next() { type_pos = %d value_pos = %d remaining sig '%s' current_type = %s\n", + reader, reader->type_pos, reader->value_pos, + _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0), + _dbus_type_to_string (t)); +#endif + + if (t == DBUS_TYPE_INVALID) + return FALSE; + + (* reader->klass->next) (reader, t); + +#if RECURSIVE_MARSHAL_READ_TRACE + _dbus_verbose (" type reader %p END next() type_pos = %d value_pos = %d remaining sig '%s' current_type = %s\n", + reader, reader->type_pos, reader->value_pos, + _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0), + _dbus_type_to_string (_dbus_type_reader_get_current_type (reader))); +#endif + + return _dbus_type_reader_get_current_type (reader) != DBUS_TYPE_INVALID; +} + +/** + * Check whether there's another value on this "level". e.g. the next + * field in a struct, the next value in an array. Returns FALSE at the + * end of the current container. + * + * You probably don't want to use this; it makes for an awkward for/while + * loop. A nicer one is "while ((current_type = get_current_type()) != INVALID)" + * + * @param reader the reader + * @returns FALSE if nothing more to read at or below this level + */ +dbus_bool_t +_dbus_type_reader_has_next (const DBusTypeReader *reader) +{ + /* Not efficient but works for now. */ + DBusTypeReader copy; + + copy = *reader; + return _dbus_type_reader_next (©); +} + +/** + * Gets the string and range of said string containing the signature + * of the current value. Essentially a more complete version of + * _dbus_type_reader_get_current_type() (returns the full type + * rather than only the outside of the onion). + * + * Note though that the first byte in a struct signature is + * #DBUS_STRUCT_BEGIN_CHAR while the current type will be + * #DBUS_TYPE_STRUCT so it isn't true that the first byte of the + * signature is always the same as the current type. Another + * difference is that this function will still return a signature when + * inside an empty array; say you recurse into empty array of int32, + * the signature is "i" but the current type will always be + * #DBUS_TYPE_INVALID since there are no elements to be currently + * pointing to. + * + * @param reader the reader + * @param str_p place to return the string with the type in it + * @param start_p place to return start of the type + * @param len_p place to return the length of the type + */ +void +_dbus_type_reader_get_signature (const DBusTypeReader *reader, + const DBusString **str_p, + int *start_p, + int *len_p) +{ + *str_p = reader->type_str; + *start_p = reader->type_pos; + *len_p = find_len_of_complete_type (reader->type_str, reader->type_pos); +} + +typedef struct +{ + DBusString replacement; /**< Marshaled value including alignment padding */ + int padding; /**< How much of the replacement block is padding */ +} ReplacementBlock; + +static dbus_bool_t +replacement_block_init (ReplacementBlock *block, + DBusTypeReader *reader) +{ + if (!_dbus_string_init (&block->replacement)) + return FALSE; + + /* % 8 is the padding to have the same align properties in + * our replacement string as we do at the position being replaced + */ + block->padding = reader->value_pos % 8; + + if (!_dbus_string_lengthen (&block->replacement, block->padding)) + goto oom; + + return TRUE; + + oom: + _dbus_string_free (&block->replacement); + return FALSE; +} + +static dbus_bool_t +replacement_block_replace (ReplacementBlock *block, + DBusTypeReader *reader, + const DBusTypeReader *realign_root) +{ + DBusTypeWriter writer; + DBusTypeReader realign_reader; + DBusList *fixups; + int orig_len; + + _dbus_assert (realign_root != NULL); + + orig_len = _dbus_string_get_length (&block->replacement); + + realign_reader = *realign_root; + +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose ("INITIALIZING replacement block writer %p at value_pos %d\n", + &writer, _dbus_string_get_length (&block->replacement)); +#endif + _dbus_type_writer_init_values_only (&writer, + realign_reader.byte_order, + realign_reader.type_str, + realign_reader.type_pos, + &block->replacement, + _dbus_string_get_length (&block->replacement)); + + _dbus_assert (realign_reader.value_pos <= reader->value_pos); + +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose ("COPYING from reader at value_pos %d to writer %p starting after value_pos %d\n", + realign_reader.value_pos, &writer, reader->value_pos); +#endif + fixups = NULL; + if (!_dbus_type_writer_write_reader_partial (&writer, + &realign_reader, + reader, + block->padding, + _dbus_string_get_length (&block->replacement) - block->padding, + &fixups)) + goto oom; + +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose ("REPLACEMENT at padding %d len %d\n", block->padding, + _dbus_string_get_length (&block->replacement) - block->padding); + _dbus_verbose_bytes_of_string (&block->replacement, block->padding, + _dbus_string_get_length (&block->replacement) - block->padding); + _dbus_verbose ("TO BE REPLACED at value_pos = %d (align pad %d) len %d realign_reader.value_pos %d\n", + reader->value_pos, reader->value_pos % 8, + realign_reader.value_pos - reader->value_pos, + realign_reader.value_pos); + _dbus_verbose_bytes_of_string (reader->value_str, + reader->value_pos, + realign_reader.value_pos - reader->value_pos); +#endif + + /* Move the replacement into position + * (realign_reader should now be at the end of the block to be replaced) + */ + if (!_dbus_string_replace_len (&block->replacement, block->padding, + _dbus_string_get_length (&block->replacement) - block->padding, + (DBusString*) reader->value_str, + reader->value_pos, + realign_reader.value_pos - reader->value_pos)) + goto oom; + + /* Process our fixups now that we can't have an OOM error */ + apply_and_free_fixups (&fixups, reader); + + return TRUE; + + oom: + _dbus_string_set_length (&block->replacement, orig_len); + free_fixups (&fixups); + return FALSE; +} + +static void +replacement_block_free (ReplacementBlock *block) +{ + _dbus_string_free (&block->replacement); +} + +/* In the variable-length case, we have to fix alignment after we insert. + * The strategy is as follows: + * + * - pad a new string to have the same alignment as the + * start of the current basic value + * - write the new basic value + * - copy from the original reader to the new string, + * which will fix the alignment of types following + * the new value + * - this copy has to start at realign_root, + * but not really write anything until it + * passes the value being set + * - as an optimization, we can stop copying + * when the source and dest values are both + * on an 8-boundary, since we know all following + * padding and alignment will be identical + * - copy the new string back to the original + * string, replacing the relevant part of the + * original string + * - now any arrays in the original string that + * contained the replaced string may have the + * wrong length; so we have to fix that + */ +static dbus_bool_t +reader_set_basic_variable_length (DBusTypeReader *reader, + int current_type, + const void *value, + const DBusTypeReader *realign_root) +{ + dbus_bool_t retval; + ReplacementBlock block; + DBusTypeWriter writer; + + _dbus_assert (realign_root != NULL); + + retval = FALSE; + + if (!replacement_block_init (&block, reader)) + return FALSE; + + /* Write the new basic value */ +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose ("INITIALIZING writer %p to write basic value at value_pos %d of replacement string\n", + &writer, _dbus_string_get_length (&block.replacement)); +#endif + _dbus_type_writer_init_values_only (&writer, + reader->byte_order, + reader->type_str, + reader->type_pos, + &block.replacement, + _dbus_string_get_length (&block.replacement)); +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose ("WRITING basic value to writer %p (replacement string)\n", &writer); +#endif + if (!_dbus_type_writer_write_basic (&writer, current_type, value)) + goto out; + + if (!replacement_block_replace (&block, + reader, + realign_root)) + goto out; + + retval = TRUE; + + out: + replacement_block_free (&block); + return retval; +} + +static void +reader_set_basic_fixed_length (DBusTypeReader *reader, + int current_type, + const void *value) +{ + _dbus_marshal_set_basic ((DBusString*) reader->value_str, + reader->value_pos, + current_type, + value, + reader->byte_order, + NULL, NULL); +} + +/** + * Sets a new value for the basic type value pointed to by the reader, + * leaving the reader valid to continue reading. Any other readers + * will be invalidated if you set a variable-length type such as a + * string. + * + * The provided realign_root is the reader to start from when + * realigning the data that follows the newly-set value. The reader + * parameter must point to a value below the realign_root parameter. + * If the type being set is fixed-length, then realign_root may be + * #NULL. Only values reachable from realign_root will be realigned, + * so if your string contains other values you will need to deal with + * those somehow yourself. It is OK if realign_root is the same + * reader as the reader parameter, though if you aren't setting the + * root it may not be such a good idea. + * + * @todo DBusTypeReader currently takes "const" versions of the type + * and value strings, and this function modifies those strings by + * casting away the const, which is of course bad if we want to get + * picky. (To be truly clean you'd have an object which contained the + * type and value strings and set_basic would be a method on that + * object... this would also make DBusTypeReader the same thing as + * DBusTypeMark. But since DBusMessage is effectively that object for + * D-Bus it doesn't seem worth creating some random object.) + * + * @todo optimize this by only rewriting until the old and new values + * are at the same alignment. Frequently this should result in only + * replacing the value that's immediately at hand. + * + * @param reader reader indicating where to set a new value + * @param value address of the value to set + * @param realign_root realign from here + * @returns #FALSE if not enough memory + */ +dbus_bool_t +_dbus_type_reader_set_basic (DBusTypeReader *reader, + const void *value, + const DBusTypeReader *realign_root) +{ + int current_type; + + _dbus_assert (!reader->klass->types_only); + _dbus_assert (reader->value_str == realign_root->value_str); + _dbus_assert (reader->value_pos >= realign_root->value_pos); + + current_type = _dbus_type_reader_get_current_type (reader); + +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose (" SET BASIC type reader %p type_pos = %d value_pos = %d remaining sig '%s' realign_root = %p with value_pos %d current_type = %s\n", + reader, reader->type_pos, reader->value_pos, + _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0), + realign_root, + realign_root ? realign_root->value_pos : -1, + _dbus_type_to_string (current_type)); + _dbus_verbose_bytes_of_string (realign_root->value_str, realign_root->value_pos, + _dbus_string_get_length (realign_root->value_str) - + realign_root->value_pos); +#endif + + _dbus_assert (dbus_type_is_basic (current_type)); + + if (dbus_type_is_fixed (current_type)) + { + reader_set_basic_fixed_length (reader, current_type, value); + return TRUE; + } + else + { + _dbus_assert (realign_root != NULL); + return reader_set_basic_variable_length (reader, current_type, + value, realign_root); + } +} + +/** + * Recursively deletes any value pointed to by the reader, leaving the + * reader valid to continue reading. Any other readers will be + * invalidated. + * + * The provided realign_root is the reader to start from when + * realigning the data that follows the newly-set value. + * See _dbus_type_reader_set_basic() for more details on the + * realign_root paramter. + * + * @todo for now this does not delete the typecodes associated with + * the value, so this function should only be used for array elements. + * + * @param reader reader indicating where to delete a value + * @param realign_root realign from here + * @returns #FALSE if not enough memory + */ +dbus_bool_t +_dbus_type_reader_delete (DBusTypeReader *reader, + const DBusTypeReader *realign_root) +{ + dbus_bool_t retval; + ReplacementBlock block; + + _dbus_assert (realign_root != NULL); + _dbus_assert (reader->klass == &array_reader_class); + + retval = FALSE; + + if (!replacement_block_init (&block, reader)) + return FALSE; + + if (!replacement_block_replace (&block, + reader, + realign_root)) + goto out; + + retval = TRUE; + + out: + replacement_block_free (&block); + return retval; +} + +/** + * Compares two readers, which must be iterating over the same value data. + * Returns #TRUE if the first parameter is further along than the second parameter. + * + * @param lhs left-hand-side (first) parameter + * @param rhs left-hand-side (first) parameter + * @returns whether lhs is greater than rhs + */ +dbus_bool_t +_dbus_type_reader_greater_than (const DBusTypeReader *lhs, + const DBusTypeReader *rhs) +{ + _dbus_assert (lhs->value_str == rhs->value_str); + + return lhs->value_pos > rhs->value_pos; +} + +/* + * + * + * DBusTypeWriter + * + * + * + */ + +/** + * Initialize a write iterator, which is used to write out values in + * serialized D-Bus format. + * + * The type_pos passed in is expected to be inside an already-valid, + * though potentially empty, type signature. This means that the byte + * after type_pos must be either #DBUS_TYPE_INVALID (aka nul) or some + * other valid type. #DBusTypeWriter won't enforce that the signature + * is already valid (you can append the nul byte at the end if you + * like), but just be aware that you need the nul byte eventually and + * #DBusTypeWriter isn't going to write it for you. + * + * @param writer the writer to init + * @param byte_order the byte order to marshal into + * @param type_str the string to write typecodes into + * @param type_pos where to insert typecodes + * @param value_str the string to write values into + * @param value_pos where to insert values + * + */ +void +_dbus_type_writer_init (DBusTypeWriter *writer, + int byte_order, + DBusString *type_str, + int type_pos, + DBusString *value_str, + int value_pos) +{ + writer->byte_order = byte_order; + writer->type_str = type_str; + writer->type_pos = type_pos; + writer->value_str = value_str; + writer->value_pos = value_pos; + writer->container_type = DBUS_TYPE_INVALID; + writer->type_pos_is_expectation = FALSE; + writer->enabled = TRUE; + +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose ("writer %p init remaining sig '%s'\n", writer, + writer->type_str ? + _dbus_string_get_const_data_len (writer->type_str, writer->type_pos, 0) : + "unknown"); +#endif +} + +/** + * Initialize a write iterator, with the signature to be provided + * later. + * + * @param writer the writer to init + * @param byte_order the byte order to marshal into + * @param value_str the string to write values into + * @param value_pos where to insert values + * + */ +void +_dbus_type_writer_init_types_delayed (DBusTypeWriter *writer, + int byte_order, + DBusString *value_str, + int value_pos) +{ + _dbus_type_writer_init (writer, byte_order, + NULL, 0, value_str, value_pos); +} + +/** + * Adds type string to the writer, if it had none. + * + * @param writer the writer to init + * @param type_str type string to add + * @param type_pos type position + * + */ +void +_dbus_type_writer_add_types (DBusTypeWriter *writer, + DBusString *type_str, + int type_pos) +{ + if (writer->type_str == NULL) /* keeps us from using this as setter */ + { + writer->type_str = type_str; + writer->type_pos = type_pos; + } +} + +/** + * Removes type string from the writer. + * + * @param writer the writer to remove from + */ +void +_dbus_type_writer_remove_types (DBusTypeWriter *writer) +{ + writer->type_str = NULL; + writer->type_pos = -1; +} + +/** + * Like _dbus_type_writer_init(), except the type string + * passed in should correspond to an existing signature that + * matches what you're going to write out. The writer will + * check what you write vs. this existing signature. + * + * @param writer the writer to init + * @param byte_order the byte order to marshal into + * @param type_str the string with signature + * @param type_pos start of signature + * @param value_str the string to write values into + * @param value_pos where to insert values + * + */ +void +_dbus_type_writer_init_values_only (DBusTypeWriter *writer, + int byte_order, + const DBusString *type_str, + int type_pos, + DBusString *value_str, + int value_pos) +{ + _dbus_type_writer_init (writer, byte_order, + (DBusString*)type_str, type_pos, + value_str, value_pos); + + writer->type_pos_is_expectation = TRUE; +} + +static dbus_bool_t +_dbus_type_writer_write_basic_no_typecode (DBusTypeWriter *writer, + int type, + const void *value) +{ + if (writer->enabled) + return _dbus_marshal_write_basic (writer->value_str, + writer->value_pos, + type, + value, + writer->byte_order, + &writer->value_pos); + else + return TRUE; +} + +/* If our parent is an array, things are a little bit complicated. + * + * The parent must have a complete element type, such as + * "i" or "aai" or "(ii)" or "a(ii)". There can't be + * unclosed parens, or an "a" with no following type. + * + * To recurse, the only allowed operation is to recurse into the + * first type in the element type. So for "i" you can't recurse, for + * "ai" you can recurse into the array, for "(ii)" you can recurse + * into the struct. + * + * If you recurse into the array for "ai", then you must specify + * "i" for the element type of the array you recurse into. + * + * While inside an array at any level, we need to avoid writing to + * type_str, since the type only appears once for the whole array, + * it does not appear for each array element. + * + * While inside an array type_pos points to the expected next + * typecode, rather than the next place we could write a typecode. + */ +static void +writer_recurse_init_and_check (DBusTypeWriter *writer, + int container_type, + DBusTypeWriter *sub) +{ + _dbus_type_writer_init (sub, + writer->byte_order, + writer->type_str, + writer->type_pos, + writer->value_str, + writer->value_pos); + + sub->container_type = container_type; + + if (writer->type_pos_is_expectation || + (sub->container_type == DBUS_TYPE_ARRAY || sub->container_type == DBUS_TYPE_VARIANT)) + sub->type_pos_is_expectation = TRUE; + else + sub->type_pos_is_expectation = FALSE; + + sub->enabled = writer->enabled; + +#ifndef DBUS_DISABLE_CHECKS + if (writer->type_pos_is_expectation && writer->type_str) + { + int expected; + + expected = _dbus_first_type_in_signature (writer->type_str, writer->type_pos); + + if (expected != sub->container_type) + { + if (expected != DBUS_TYPE_INVALID) + _dbus_warn_check_failed ("Writing an element of type %s, but the expected type here is %s\n" + "The overall signature expected here was '%s' and we are on byte %d of that signature.\n", + _dbus_type_to_string (sub->container_type), + _dbus_type_to_string (expected), + _dbus_string_get_const_data (writer->type_str), writer->type_pos); + else + _dbus_warn_check_failed ("Writing an element of type %s, but no value is expected here\n" + "The overall signature expected here was '%s' and we are on byte %d of that signature.\n", + _dbus_type_to_string (sub->container_type), + _dbus_string_get_const_data (writer->type_str), writer->type_pos); + + _dbus_assert_not_reached ("bad array element or variant content written"); + } + } +#endif /* DBUS_DISABLE_CHECKS */ + +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose (" type writer %p recurse parent %s type_pos = %d value_pos = %d is_expectation = %d remaining sig '%s' enabled = %d\n", + writer, + _dbus_type_to_string (writer->container_type), + writer->type_pos, writer->value_pos, writer->type_pos_is_expectation, + writer->type_str ? + _dbus_string_get_const_data_len (writer->type_str, writer->type_pos, 0) : + "unknown", + writer->enabled); + _dbus_verbose (" type writer %p recurse sub %s type_pos = %d value_pos = %d is_expectation = %d enabled = %d\n", + sub, + _dbus_type_to_string (sub->container_type), + sub->type_pos, sub->value_pos, + sub->type_pos_is_expectation, + sub->enabled); +#endif +} + +static dbus_bool_t +write_or_verify_typecode (DBusTypeWriter *writer, + int typecode) +{ + /* A subwriter inside an array or variant will have type_pos + * pointing to the expected typecode; a writer not inside an array + * or variant has type_pos pointing to the next place to insert a + * typecode. + */ +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose (" type writer %p write_or_verify start type_pos = %d remaining sig '%s' enabled = %d\n", + writer, writer->type_pos, + writer->type_str ? + _dbus_string_get_const_data_len (writer->type_str, writer->type_pos, 0) : + "unknown", + writer->enabled); +#endif + + if (writer->type_str == NULL) + return TRUE; + + if (writer->type_pos_is_expectation) + { +#ifndef DBUS_DISABLE_CHECKS + { + int expected; + + expected = _dbus_string_get_byte (writer->type_str, writer->type_pos); + + if (expected != typecode) + { + if (expected != DBUS_TYPE_INVALID) + _dbus_warn_check_failed ("Array or variant type requires that type %s be written, but %s was written.\n" + "The overall signature expected here was '%s' and we are on byte %d of that signature.\n", + _dbus_type_to_string (expected), _dbus_type_to_string (typecode), + _dbus_string_get_const_data (writer->type_str), writer->type_pos); + else + _dbus_warn_check_failed ("Array or variant type wasn't expecting any more values to be written into it, but a value %s was written.\n" + "The overall signature expected here was '%s' and we are on byte %d of that signature.\n", + _dbus_type_to_string (typecode), + _dbus_string_get_const_data (writer->type_str), writer->type_pos); + _dbus_assert_not_reached ("bad type inserted somewhere inside an array or variant"); + } + } +#endif /* DBUS_DISABLE_CHECKS */ + + /* if immediately inside an array we'd always be appending an element, + * so the expected type doesn't change; if inside a struct or something + * below an array, we need to move through said struct or something. + */ + if (writer->container_type != DBUS_TYPE_ARRAY) + writer->type_pos += 1; + } + else + { + if (!_dbus_string_insert_byte (writer->type_str, + writer->type_pos, + typecode)) + return FALSE; + + writer->type_pos += 1; + } + +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose (" type writer %p write_or_verify end type_pos = %d remaining sig '%s'\n", + writer, writer->type_pos, + _dbus_string_get_const_data_len (writer->type_str, writer->type_pos, 0)); +#endif + + return TRUE; +} + +static dbus_bool_t +writer_recurse_struct_or_dict_entry (DBusTypeWriter *writer, + int begin_char, + const DBusString *contained_type, + int contained_type_start, + int contained_type_len, + DBusTypeWriter *sub) +{ + /* FIXME right now contained_type is ignored; we could probably + * almost trivially fix the code so if it's present we + * write it out and then set type_pos_is_expectation + */ + + /* Ensure that we'll be able to add alignment padding and the typecode */ + if (writer->enabled) + { + if (!_dbus_string_alloc_space (sub->value_str, 8)) + return FALSE; + } + + if (!write_or_verify_typecode (sub, begin_char)) + _dbus_assert_not_reached ("failed to insert struct typecode after prealloc"); + + if (writer->enabled) + { + if (!_dbus_string_insert_bytes (sub->value_str, + sub->value_pos, + _DBUS_ALIGN_VALUE (sub->value_pos, 8) - sub->value_pos, + '\0')) + _dbus_assert_not_reached ("should not have failed to insert alignment padding for struct"); + sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, 8); + } + + return TRUE; +} + + +static dbus_bool_t +writer_recurse_array (DBusTypeWriter *writer, + const DBusString *contained_type, + int contained_type_start, + int contained_type_len, + DBusTypeWriter *sub, + dbus_bool_t is_array_append) +{ + dbus_uint32_t value = 0; + int alignment; + int aligned; + +#ifndef DBUS_DISABLE_CHECKS + if (writer->container_type == DBUS_TYPE_ARRAY && + writer->type_str) + { + if (!_dbus_string_equal_substring (contained_type, + contained_type_start, + contained_type_len, + writer->type_str, + writer->u.array.element_type_pos + 1)) + { + _dbus_warn_check_failed ("Writing an array of '%s' but this is incompatible with the expected type of elements in the parent array\n", + _dbus_string_get_const_data_len (contained_type, + contained_type_start, + contained_type_len)); + _dbus_assert_not_reached ("incompatible type for child array"); + } + } +#endif /* DBUS_DISABLE_CHECKS */ + + if (writer->enabled && !is_array_append) + { + /* 3 pad + 4 bytes for the array length, and 4 bytes possible padding + * before array values + */ + if (!_dbus_string_alloc_space (sub->value_str, 3 + 4 + 4)) + return FALSE; + } + + if (writer->type_str != NULL) + { + sub->type_pos += 1; /* move to point to the element type, since type_pos + * should be the expected type for further writes + */ + sub->u.array.element_type_pos = sub->type_pos; + } + + if (!writer->type_pos_is_expectation) + { + /* sub is a toplevel/outermost array so we need to write the type data */ + + /* alloc space for array typecode, element signature */ + if (!_dbus_string_alloc_space (writer->type_str, 1 + contained_type_len)) + return FALSE; + + if (!_dbus_string_insert_byte (writer->type_str, + writer->type_pos, + DBUS_TYPE_ARRAY)) + _dbus_assert_not_reached ("failed to insert array typecode after prealloc"); + + if (!_dbus_string_copy_len (contained_type, + contained_type_start, contained_type_len, + sub->type_str, + sub->u.array.element_type_pos)) + _dbus_assert_not_reached ("should not have failed to insert array element typecodes"); + } + + if (writer->type_str != NULL) + { + /* If the parent is an array, we hold type_pos pointing at the array element type; + * otherwise advance it to reflect the array value we just recursed into + */ + if (writer->container_type != DBUS_TYPE_ARRAY) + writer->type_pos += 1 + contained_type_len; + else + _dbus_assert (writer->type_pos_is_expectation); /* because it's an array */ + } + + if (writer->enabled) + { + /* Write (or jump over, if is_array_append) the length */ + sub->u.array.len_pos = _DBUS_ALIGN_VALUE (sub->value_pos, 4); + + if (is_array_append) + { + sub->value_pos += 4; + } + else + { + if (!_dbus_type_writer_write_basic_no_typecode (sub, DBUS_TYPE_UINT32, + &value)) + _dbus_assert_not_reached ("should not have failed to insert array len"); + } + + _dbus_assert (sub->u.array.len_pos == sub->value_pos - 4); + + /* Write alignment padding for array elements + * Note that we write the padding *even for empty arrays* + * to avoid wonky special cases + */ + alignment = element_type_get_alignment (contained_type, contained_type_start); + + aligned = _DBUS_ALIGN_VALUE (sub->value_pos, alignment); + if (aligned != sub->value_pos) + { + if (!is_array_append) + { + if (!_dbus_string_insert_bytes (sub->value_str, + sub->value_pos, + aligned - sub->value_pos, + '\0')) + _dbus_assert_not_reached ("should not have failed to insert alignment padding"); + } + + sub->value_pos = aligned; + } + + sub->u.array.start_pos = sub->value_pos; + + if (is_array_append) + { + dbus_uint32_t len; + + _dbus_assert (_DBUS_ALIGN_VALUE (sub->u.array.len_pos, 4) == + (unsigned) sub->u.array.len_pos); + len = _dbus_unpack_uint32 (sub->byte_order, + _dbus_string_get_const_data_len (sub->value_str, + sub->u.array.len_pos, + 4)); + + sub->value_pos += len; + } + } + else + { + /* not enabled, so we won't write the len_pos; set it to -1 to so indicate */ + sub->u.array.len_pos = -1; + sub->u.array.start_pos = sub->value_pos; + } + + _dbus_assert (sub->u.array.len_pos < sub->u.array.start_pos); + _dbus_assert (is_array_append || sub->u.array.start_pos == sub->value_pos); + +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose (" type writer %p recurse array done remaining sig '%s' array start_pos = %d len_pos = %d value_pos = %d\n", sub, + sub->type_str ? + _dbus_string_get_const_data_len (sub->type_str, sub->type_pos, 0) : + "unknown", + sub->u.array.start_pos, sub->u.array.len_pos, sub->value_pos); +#endif + + return TRUE; +} + +/* Variant value will normally have: + * 1 byte signature length not including nul + * signature typecodes (nul terminated) + * padding to alignment of contained type + * body according to signature + * + * The signature string can only have a single type + * in it but that type may be complex/recursive. + * + * So a typical variant type with the integer 3 will have these + * octets: + * 0x1 'i' '\0' [1 byte padding to alignment boundary] 0x0 0x0 0x0 0x3 + * + * The main world of hurt for writing out a variant is that the type + * string is the same string as the value string. Which means + * inserting to the type string will move the value_pos; and it means + * that inserting to the type string could break type alignment. + */ +static dbus_bool_t +writer_recurse_variant (DBusTypeWriter *writer, + const DBusString *contained_type, + int contained_type_start, + int contained_type_len, + DBusTypeWriter *sub) +{ + int contained_alignment; + + if (writer->enabled) + { + /* Allocate space for the worst case, which is 1 byte sig + * length, nul byte at end of sig, and 7 bytes padding to + * 8-boundary. + */ + if (!_dbus_string_alloc_space (sub->value_str, contained_type_len + 9)) + return FALSE; + } + + /* write VARIANT typecode to the parent's type string */ + if (!write_or_verify_typecode (writer, DBUS_TYPE_VARIANT)) + return FALSE; + + /* If not enabled, mark that we have no type_str anymore ... */ + + if (!writer->enabled) + { + sub->type_str = NULL; + sub->type_pos = -1; + + return TRUE; + } + + /* If we're enabled then continue ... */ + + if (!_dbus_string_insert_byte (sub->value_str, + sub->value_pos, + contained_type_len)) + _dbus_assert_not_reached ("should not have failed to insert variant type sig len"); + + sub->value_pos += 1; + + /* Here we switch over to the expected type sig we're about to write */ + sub->type_str = sub->value_str; + sub->type_pos = sub->value_pos; + + if (!_dbus_string_copy_len (contained_type, contained_type_start, contained_type_len, + sub->value_str, sub->value_pos)) + _dbus_assert_not_reached ("should not have failed to insert variant type sig"); + + sub->value_pos += contained_type_len; + + if (!_dbus_string_insert_byte (sub->value_str, + sub->value_pos, + DBUS_TYPE_INVALID)) + _dbus_assert_not_reached ("should not have failed to insert variant type nul termination"); + + sub->value_pos += 1; + + contained_alignment = _dbus_type_get_alignment (_dbus_first_type_in_signature (contained_type, contained_type_start)); + + if (!_dbus_string_insert_bytes (sub->value_str, + sub->value_pos, + _DBUS_ALIGN_VALUE (sub->value_pos, contained_alignment) - sub->value_pos, + '\0')) + _dbus_assert_not_reached ("should not have failed to insert alignment padding for variant body"); + sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, contained_alignment); + + return TRUE; +} + +static dbus_bool_t +_dbus_type_writer_recurse_contained_len (DBusTypeWriter *writer, + int container_type, + const DBusString *contained_type, + int contained_type_start, + int contained_type_len, + DBusTypeWriter *sub, + dbus_bool_t is_array_append) +{ + writer_recurse_init_and_check (writer, container_type, sub); + + switch (container_type) + { + case DBUS_TYPE_STRUCT: + return writer_recurse_struct_or_dict_entry (writer, + DBUS_STRUCT_BEGIN_CHAR, + contained_type, + contained_type_start, contained_type_len, + sub); + break; + case DBUS_TYPE_DICT_ENTRY: + return writer_recurse_struct_or_dict_entry (writer, + DBUS_DICT_ENTRY_BEGIN_CHAR, + contained_type, + contained_type_start, contained_type_len, + sub); + break; + case DBUS_TYPE_ARRAY: + return writer_recurse_array (writer, + contained_type, contained_type_start, contained_type_len, + sub, is_array_append); + break; + case DBUS_TYPE_VARIANT: + return writer_recurse_variant (writer, + contained_type, contained_type_start, contained_type_len, + sub); + break; + default: + _dbus_assert_not_reached ("tried to recurse into type that doesn't support that"); + return FALSE; + break; + } +} + +/** + * Opens a new container and writes out the initial information for that container. + * + * @param writer the writer + * @param container_type the type of the container to open + * @param contained_type the array element type or variant content type + * @param contained_type_start position to look for the type + * @param sub the new sub-writer to write container contents + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_type_writer_recurse (DBusTypeWriter *writer, + int container_type, + const DBusString *contained_type, + int contained_type_start, + DBusTypeWriter *sub) +{ + int contained_type_len; + + if (contained_type) + contained_type_len = find_len_of_complete_type (contained_type, contained_type_start); + else + contained_type_len = 0; + + return _dbus_type_writer_recurse_contained_len (writer, container_type, + contained_type, + contained_type_start, + contained_type_len, + sub, + FALSE); +} + +/** + * Append to an existing array. Essentially, the writer will read an + * existing length at the write location; jump over that length; and + * write new fields. On unrecurse(), the existing length will be + * updated. + * + * @param writer the writer + * @param contained_type element type + * @param contained_type_start position of element type + * @param sub the subwriter to init + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_type_writer_append_array (DBusTypeWriter *writer, + const DBusString *contained_type, + int contained_type_start, + DBusTypeWriter *sub) +{ + int contained_type_len; + + if (contained_type) + contained_type_len = find_len_of_complete_type (contained_type, contained_type_start); + else + contained_type_len = 0; + + return _dbus_type_writer_recurse_contained_len (writer, DBUS_TYPE_ARRAY, + contained_type, + contained_type_start, + contained_type_len, + sub, + TRUE); +} + +static int +writer_get_array_len (DBusTypeWriter *writer) +{ + _dbus_assert (writer->container_type == DBUS_TYPE_ARRAY); + return writer->value_pos - writer->u.array.start_pos; +} + +/** + * Closes a container created by _dbus_type_writer_recurse() + * and writes any additional information to the values block. + * + * @param writer the writer + * @param sub the sub-writer created by _dbus_type_writer_recurse() + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_type_writer_unrecurse (DBusTypeWriter *writer, + DBusTypeWriter *sub) +{ + /* type_pos_is_expectation never gets unset once set, or we'd get all hosed */ + _dbus_assert (!writer->type_pos_is_expectation || + (writer->type_pos_is_expectation && sub->type_pos_is_expectation)); + +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose (" type writer %p unrecurse type_pos = %d value_pos = %d is_expectation = %d container_type = %s\n", + writer, writer->type_pos, writer->value_pos, writer->type_pos_is_expectation, + _dbus_type_to_string (writer->container_type)); + _dbus_verbose (" type writer %p unrecurse sub type_pos = %d value_pos = %d is_expectation = %d container_type = %s\n", + sub, sub->type_pos, sub->value_pos, + sub->type_pos_is_expectation, + _dbus_type_to_string (sub->container_type)); +#endif + + if (sub->container_type == DBUS_TYPE_STRUCT) + { + if (!write_or_verify_typecode (sub, DBUS_STRUCT_END_CHAR)) + return FALSE; + } + else if (sub->container_type == DBUS_TYPE_DICT_ENTRY) + { + if (!write_or_verify_typecode (sub, DBUS_DICT_ENTRY_END_CHAR)) + return FALSE; + } + else if (sub->container_type == DBUS_TYPE_ARRAY) + { + if (sub->u.array.len_pos >= 0) /* len_pos == -1 if we weren't enabled when we passed it */ + { + dbus_uint32_t len; + + /* Set the array length */ + len = writer_get_array_len (sub); + _dbus_marshal_set_uint32 (sub->value_str, + sub->u.array.len_pos, + len, + sub->byte_order); +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose (" filled in sub array len to %u at len_pos %d\n", + len, sub->u.array.len_pos); +#endif + } +#if RECURSIVE_MARSHAL_WRITE_TRACE + else + { + _dbus_verbose (" not filling in sub array len because we were disabled when we passed the len\n"); + } +#endif + } + + /* Now get type_pos right for the parent writer. Here are the cases: + * + * Cases !writer->type_pos_is_expectation: + * (in these cases we want to update to the new insertion point) + * + * - if we recursed into a STRUCT then we didn't know in advance + * what the types in the struct would be; so we have to fill in + * that information now. + * writer->type_pos = sub->type_pos + * + * - if we recursed into anything else, we knew the full array + * type, or knew the single typecode marking VARIANT, so + * writer->type_pos is already correct. + * writer->type_pos should remain as-is + * + * - note that the parent is never an ARRAY or VARIANT, if it were + * then type_pos_is_expectation would be TRUE. The parent + * is thus known to be a toplevel or STRUCT. + * + * Cases where writer->type_pos_is_expectation: + * (in these cases we want to update to next expected type to write) + * + * - we recursed from STRUCT into STRUCT and we didn't increment + * type_pos in the parent just to stay consistent with the + * !writer->type_pos_is_expectation case (though we could + * special-case this in recurse_struct instead if we wanted) + * writer->type_pos = sub->type_pos + * + * - we recursed from STRUCT into ARRAY or VARIANT and type_pos + * for parent should have been incremented already + * writer->type_pos should remain as-is + * + * - we recursed from ARRAY into a sub-element, so type_pos in the + * parent is the element type and should remain the element type + * for the benefit of the next child element + * writer->type_pos should remain as-is + * + * - we recursed from VARIANT into its value, so type_pos in the + * parent makes no difference since there's only one value + * and we just finished writing it and won't use type_pos again + * writer->type_pos should remain as-is + * + * + * For all these, DICT_ENTRY is the same as STRUCT + */ + if (writer->type_str != NULL) + { + if ((sub->container_type == DBUS_TYPE_STRUCT || + sub->container_type == DBUS_TYPE_DICT_ENTRY) && + (writer->container_type == DBUS_TYPE_STRUCT || + writer->container_type == DBUS_TYPE_DICT_ENTRY || + writer->container_type == DBUS_TYPE_INVALID)) + { + /* Advance the parent to the next struct field */ + writer->type_pos = sub->type_pos; + } + } + + writer->value_pos = sub->value_pos; + +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose (" type writer %p unrecursed type_pos = %d value_pos = %d remaining sig '%s'\n", + writer, writer->type_pos, writer->value_pos, + writer->type_str ? + _dbus_string_get_const_data_len (writer->type_str, writer->type_pos, 0) : + "unknown"); +#endif + + return TRUE; +} + +/** + * Writes out a basic type. + * + * @param writer the writer + * @param type the type to write + * @param value the address of the value to write + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_type_writer_write_basic (DBusTypeWriter *writer, + int type, + const void *value) +{ + dbus_bool_t retval; + + /* First ensure that our type realloc will succeed */ + if (!writer->type_pos_is_expectation && writer->type_str != NULL) + { + if (!_dbus_string_alloc_space (writer->type_str, 1)) + return FALSE; + } + + retval = FALSE; + + if (!_dbus_type_writer_write_basic_no_typecode (writer, type, value)) + goto out; + + if (!write_or_verify_typecode (writer, type)) + _dbus_assert_not_reached ("failed to write typecode after prealloc"); + + retval = TRUE; + + out: +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose (" type writer %p basic type_pos = %d value_pos = %d is_expectation = %d enabled = %d\n", + writer, writer->type_pos, writer->value_pos, writer->type_pos_is_expectation, + writer->enabled); +#endif + + return retval; +} + +/** + * Writes a block of fixed-length basic values, i.e. those that are + * both dbus_type_is_fixed() and _dbus_type_is_basic(). The block + * must be written inside an array. + * + * The value parameter should be the address of said array of values, + * so e.g. if it's an array of double, pass in "const double**" + * + * @param writer the writer + * @param element_type type of stuff in the array + * @param value address of the array + * @param n_elements number of elements in the array + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_type_writer_write_fixed_multi (DBusTypeWriter *writer, + int element_type, + const void *value, + int n_elements) +{ + _dbus_assert (writer->container_type == DBUS_TYPE_ARRAY); + _dbus_assert (dbus_type_is_fixed (element_type)); + _dbus_assert (writer->type_pos_is_expectation); + _dbus_assert (n_elements >= 0); + +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose (" type writer %p entering fixed multi type_pos = %d value_pos = %d n_elements %d\n", + writer, writer->type_pos, writer->value_pos, n_elements); +#endif + + if (!write_or_verify_typecode (writer, element_type)) + _dbus_assert_not_reached ("OOM should not happen if only verifying typecode"); + + if (writer->enabled) + { + if (!_dbus_marshal_write_fixed_multi (writer->value_str, + writer->value_pos, + element_type, + value, + n_elements, + writer->byte_order, + &writer->value_pos)) + return FALSE; + } + +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose (" type writer %p fixed multi written new type_pos = %d new value_pos = %d n_elements %d\n", + writer, writer->type_pos, writer->value_pos, n_elements); +#endif + + return TRUE; +} + +static void +enable_if_after (DBusTypeWriter *writer, + DBusTypeReader *reader, + const DBusTypeReader *start_after) +{ + if (start_after) + { + if (!writer->enabled && _dbus_type_reader_greater_than (reader, start_after)) + { + _dbus_type_writer_set_enabled (writer, TRUE); +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose ("ENABLING writer %p at %d because reader at value_pos %d is after reader at value_pos %d\n", + writer, writer->value_pos, reader->value_pos, start_after->value_pos); +#endif + } + + _dbus_assert ((!writer->enabled && !_dbus_type_reader_greater_than (reader, start_after)) || + (writer->enabled && _dbus_type_reader_greater_than (reader, start_after))); + } +} + +static dbus_bool_t +append_fixup (DBusList **fixups, + const DBusArrayLenFixup *fixup) +{ + DBusArrayLenFixup *f; + + f = dbus_new (DBusArrayLenFixup, 1); + if (f == NULL) + return FALSE; + + *f = *fixup; + + if (!_dbus_list_append (fixups, f)) + { + dbus_free (f); + return FALSE; + } + + _dbus_assert (f->len_pos_in_reader == fixup->len_pos_in_reader); + _dbus_assert (f->new_len == fixup->new_len); + + return TRUE; +} + +/* This loop is trivial if you ignore all the start_after nonsense, + * so if you're trying to figure it out, start by ignoring that + */ +static dbus_bool_t +writer_write_reader_helper (DBusTypeWriter *writer, + DBusTypeReader *reader, + const DBusTypeReader *start_after, + int start_after_new_pos, + int start_after_new_len, + DBusList **fixups, + dbus_bool_t inside_start_after) +{ + int current_type; + + while ((current_type = _dbus_type_reader_get_current_type (reader)) != DBUS_TYPE_INVALID) + { + if (dbus_type_is_container (current_type)) + { + DBusTypeReader subreader; + DBusTypeWriter subwriter; + const DBusString *sig_str; + int sig_start; + int sig_len; + dbus_bool_t enabled_at_recurse; + dbus_bool_t past_start_after; + int reader_array_len_pos; + int reader_array_start_pos; + dbus_bool_t this_is_start_after; + + /* type_pos is checked since e.g. in a struct the struct + * and its first field have the same value_pos. + * type_str will differ in reader/start_after for variants + * where type_str is inside the value_str + */ + if (!inside_start_after && start_after && + reader->value_pos == start_after->value_pos && + reader->type_str == start_after->type_str && + reader->type_pos == start_after->type_pos) + this_is_start_after = TRUE; + else + this_is_start_after = FALSE; + + _dbus_type_reader_recurse (reader, &subreader); + + if (current_type == DBUS_TYPE_ARRAY) + { + reader_array_len_pos = ARRAY_READER_LEN_POS (&subreader); + reader_array_start_pos = subreader.u.array.start_pos; + } + else + { + /* quiet gcc */ + reader_array_len_pos = -1; + reader_array_start_pos = -1; + } + + _dbus_type_reader_get_signature (&subreader, &sig_str, + &sig_start, &sig_len); + +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose ("about to recurse into %s reader at %d subreader at %d writer at %d start_after reader at %d write target len %d inside_start_after = %d this_is_start_after = %d\n", + _dbus_type_to_string (current_type), + reader->value_pos, + subreader.value_pos, + writer->value_pos, + start_after ? start_after->value_pos : -1, + _dbus_string_get_length (writer->value_str), + inside_start_after, this_is_start_after); +#endif + + if (!inside_start_after && !this_is_start_after) + enable_if_after (writer, &subreader, start_after); + enabled_at_recurse = writer->enabled; + if (!_dbus_type_writer_recurse_contained_len (writer, current_type, + sig_str, sig_start, sig_len, + &subwriter, FALSE)) + goto oom; + +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose ("recursed into subwriter at %d write target len %d\n", + subwriter.value_pos, + _dbus_string_get_length (subwriter.value_str)); +#endif + + if (!writer_write_reader_helper (&subwriter, &subreader, start_after, + start_after_new_pos, start_after_new_len, + fixups, + inside_start_after || + this_is_start_after)) + goto oom; + +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose ("about to unrecurse from %s subreader at %d writer at %d subwriter at %d write target len %d\n", + _dbus_type_to_string (current_type), + subreader.value_pos, + writer->value_pos, + subwriter.value_pos, + _dbus_string_get_length (writer->value_str)); +#endif + + if (!inside_start_after && !this_is_start_after) + enable_if_after (writer, &subreader, start_after); + past_start_after = writer->enabled; + if (!_dbus_type_writer_unrecurse (writer, &subwriter)) + goto oom; + + /* If we weren't enabled when we recursed, we didn't + * write an array len; if we passed start_after + * somewhere inside the array, then we need to generate + * a fixup. + */ + if (start_after != NULL && + !enabled_at_recurse && past_start_after && + current_type == DBUS_TYPE_ARRAY && + fixups != NULL) + { + DBusArrayLenFixup fixup; + int bytes_written_after_start_after; + int bytes_before_start_after; + int old_len; + + /* this subwriter access is moderately unkosher since we + * already unrecursed, but it works as long as unrecurse + * doesn't break us on purpose + */ + bytes_written_after_start_after = writer_get_array_len (&subwriter); + + bytes_before_start_after = + start_after->value_pos - reader_array_start_pos; + + fixup.len_pos_in_reader = reader_array_len_pos; + fixup.new_len = + bytes_before_start_after + + start_after_new_len + + bytes_written_after_start_after; + + _dbus_assert (_DBUS_ALIGN_VALUE (fixup.len_pos_in_reader, 4) == + (unsigned) fixup.len_pos_in_reader); + + old_len = _dbus_unpack_uint32 (reader->byte_order, + _dbus_string_get_const_data_len (reader->value_str, + fixup.len_pos_in_reader, 4)); + + if (old_len != fixup.new_len && !append_fixup (fixups, &fixup)) + goto oom; + +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose ("Generated fixup len_pos_in_reader = %d new_len = %d reader_array_start_pos = %d start_after->value_pos = %d bytes_before_start_after = %d start_after_new_len = %d bytes_written_after_start_after = %d\n", + fixup.len_pos_in_reader, + fixup.new_len, + reader_array_start_pos, + start_after->value_pos, + bytes_before_start_after, + start_after_new_len, + bytes_written_after_start_after); +#endif + } + } + else + { + DBusBasicValue val; + + _dbus_assert (dbus_type_is_basic (current_type)); + +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose ("Reading basic value %s at %d\n", + _dbus_type_to_string (current_type), + reader->value_pos); +#endif + + _dbus_type_reader_read_basic (reader, &val); + +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose ("Writing basic value %s at %d write target len %d inside_start_after = %d\n", + _dbus_type_to_string (current_type), + writer->value_pos, + _dbus_string_get_length (writer->value_str), + inside_start_after); +#endif + if (!inside_start_after) + enable_if_after (writer, reader, start_after); + if (!_dbus_type_writer_write_basic (writer, current_type, &val)) + goto oom; +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose ("Wrote basic value %s, new value_pos %d write target len %d\n", + _dbus_type_to_string (current_type), + writer->value_pos, + _dbus_string_get_length (writer->value_str)); +#endif + } + + _dbus_type_reader_next (reader); + } + + return TRUE; + + oom: + if (fixups) + apply_and_free_fixups (fixups, NULL); /* NULL for reader to apply to */ + + return FALSE; +} + +/** + * Iterate through all values in the given reader, writing a copy of + * each value to the writer. The reader will be moved forward to its + * end position. + * + * If a reader start_after is provided, it should be a reader for the + * same data as the reader to be written. Only values occurring after + * the value pointed to by start_after will be written to the writer. + * + * If start_after is provided, then the copy of the reader will be + * partial. This means that array lengths will not have been copied. + * The assumption is that you wrote a new version of the value at + * start_after to the writer. You have to pass in the start position + * and length of the new value. (If you are deleting the value + * at start_after, pass in 0 for the length.) + * + * If the fixups parameter is non-#NULL, then any array length that + * was read but not written due to start_after will be provided + * as a #DBusArrayLenFixup. The fixup contains the position of the + * array length in the source data, and the correct array length + * assuming you combine the source data before start_after with + * the written data at start_after and beyond. + * + * @param writer the writer to copy to + * @param reader the reader to copy from + * @param start_after #NULL or a reader showing where to start + * @param start_after_new_pos the position of start_after equivalent in the target data + * @param start_after_new_len the length of start_after equivalent in the target data + * @param fixups list to append #DBusArrayLenFixup if the write was partial + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_type_writer_write_reader_partial (DBusTypeWriter *writer, + DBusTypeReader *reader, + const DBusTypeReader *start_after, + int start_after_new_pos, + int start_after_new_len, + DBusList **fixups) +{ + DBusTypeWriter orig; + int orig_type_len; + int orig_value_len; + int new_bytes; + int orig_enabled; + + orig = *writer; + orig_type_len = _dbus_string_get_length (writer->type_str); + orig_value_len = _dbus_string_get_length (writer->value_str); + orig_enabled = writer->enabled; + + if (start_after) + _dbus_type_writer_set_enabled (writer, FALSE); + + if (!writer_write_reader_helper (writer, reader, start_after, + start_after_new_pos, + start_after_new_len, + fixups, FALSE)) + goto oom; + + _dbus_type_writer_set_enabled (writer, orig_enabled); + return TRUE; + + oom: + if (!writer->type_pos_is_expectation) + { + new_bytes = _dbus_string_get_length (writer->type_str) - orig_type_len; + _dbus_string_delete (writer->type_str, orig.type_pos, new_bytes); + } + new_bytes = _dbus_string_get_length (writer->value_str) - orig_value_len; + _dbus_string_delete (writer->value_str, orig.value_pos, new_bytes); + + *writer = orig; + + return FALSE; +} + +/** + * Iterate through all values in the given reader, writing a copy of + * each value to the writer. The reader will be moved forward to its + * end position. + * + * @param writer the writer to copy to + * @param reader the reader to copy from + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_type_writer_write_reader (DBusTypeWriter *writer, + DBusTypeReader *reader) +{ + return _dbus_type_writer_write_reader_partial (writer, reader, NULL, 0, 0, NULL); +} + +/** + * If disabled, a writer can still be iterated forward and recursed/unrecursed + * but won't write any values. Types will still be written unless the + * writer is a "values only" writer, because the writer needs access to + * a valid signature to be able to iterate. + * + * @param writer the type writer + * @param enabled #TRUE if values should be written + */ +void +_dbus_type_writer_set_enabled (DBusTypeWriter *writer, + dbus_bool_t enabled) +{ + writer->enabled = enabled != FALSE; +} + +/** @} */ /* end of DBusMarshal group */ + +/* tests in dbus-marshal-recursive-util.c */ diff --git a/src/dbus/dbus-marshal-recursive.h b/src/dbus/dbus-marshal-recursive.h new file mode 100644 index 0000000..14f38b2 --- /dev/null +++ b/src/dbus/dbus-marshal-recursive.h @@ -0,0 +1,196 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-marshal-recursive.h Marshalling routines for recursive types + * + * Copyright (C) 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef DBUS_MARSHAL_RECURSIVE_H +#define DBUS_MARSHAL_RECURSIVE_H + +#include +#include +#include + +#ifndef PACKAGE +#error "config.h not included here" +#endif + +typedef struct DBusTypeReader DBusTypeReader; +typedef struct DBusTypeWriter DBusTypeWriter; +typedef struct DBusTypeReaderClass DBusTypeReaderClass; +typedef struct DBusArrayLenFixup DBusArrayLenFixup; + +/** + * The type reader is an iterator for reading values from a block of + * values. + */ +struct DBusTypeReader +{ + dbus_uint32_t byte_order : 8; /**< byte order of the block */ + + dbus_uint32_t finished : 1; /**< marks we're at end iterator for cases + * where we don't have another way to tell + */ + dbus_uint32_t array_len_offset : 3; /**< bytes back from start_pos that len ends */ + const DBusString *type_str; /**< string containing signature of block */ + int type_pos; /**< current position in signature */ + const DBusString *value_str; /**< string containing values of block */ + int value_pos; /**< current position in values */ + + const DBusTypeReaderClass *klass; /**< the vtable for the reader */ + union + { + struct { + int start_pos; /**< for array readers, the start of the array values */ + } array; + } u; /**< class-specific data */ +}; + +/** + * The type writer is an iterator for writing to a block of values. + */ +struct DBusTypeWriter +{ + dbus_uint32_t byte_order : 8; /**< byte order to write values with */ + + dbus_uint32_t container_type : 8; /**< what are we inside? (e.g. struct, variant, array) */ + + dbus_uint32_t type_pos_is_expectation : 1; /**< type_pos can be either an insertion point for or an expected next type */ + + dbus_uint32_t enabled : 1; /**< whether to write values */ + + DBusString *type_str; /**< where to write typecodes (or read type expectations) */ + int type_pos; /**< current pos in type_str */ + DBusString *value_str; /**< where to write values */ + int value_pos; /**< next position to write */ + + union + { + struct { + int start_pos; /**< position of first element in the array */ + int len_pos; /**< position of length of the array */ + int element_type_pos; /**< position of array element type in type_str */ + } array; + } u; /**< class-specific data */ +}; + +/** + * When modifying an existing block of values, array lengths may need + * to be adjusted; those adjustments are described by this struct. + */ +struct DBusArrayLenFixup +{ + int len_pos_in_reader; /**< where the length was in the original block */ + int new_len; /**< the new value of the length in the written-out block */ +}; + +void _dbus_type_reader_init (DBusTypeReader *reader, + int byte_order, + const DBusString *type_str, + int type_pos, + const DBusString *value_str, + int value_pos); +void _dbus_type_reader_init_types_only (DBusTypeReader *reader, + const DBusString *type_str, + int type_pos); +int _dbus_type_reader_get_current_type (const DBusTypeReader *reader); +int _dbus_type_reader_get_element_type (const DBusTypeReader *reader); +int _dbus_type_reader_get_value_pos (const DBusTypeReader *reader); +void _dbus_type_reader_read_basic (const DBusTypeReader *reader, + void *value); +int _dbus_type_reader_get_array_length (const DBusTypeReader *reader); +void _dbus_type_reader_read_fixed_multi (const DBusTypeReader *reader, + void *value, + int *n_elements); +void _dbus_type_reader_read_raw (const DBusTypeReader *reader, + const unsigned char **value_location); +void _dbus_type_reader_recurse (DBusTypeReader *reader, + DBusTypeReader *subreader); +dbus_bool_t _dbus_type_reader_next (DBusTypeReader *reader); +dbus_bool_t _dbus_type_reader_has_next (const DBusTypeReader *reader); +void _dbus_type_reader_get_signature (const DBusTypeReader *reader, + const DBusString **str_p, + int *start_p, + int *len_p); +dbus_bool_t _dbus_type_reader_set_basic (DBusTypeReader *reader, + const void *value, + const DBusTypeReader *realign_root); +dbus_bool_t _dbus_type_reader_delete (DBusTypeReader *reader, + const DBusTypeReader *realign_root); +dbus_bool_t _dbus_type_reader_greater_than (const DBusTypeReader *lhs, + const DBusTypeReader *rhs); + +dbus_bool_t _dbus_type_reader_equal_values (const DBusTypeReader *lhs, + const DBusTypeReader *rhs); + +void _dbus_type_signature_next (const char *signature, + int *type_pos); + +void _dbus_type_writer_init (DBusTypeWriter *writer, + int byte_order, + DBusString *type_str, + int type_pos, + DBusString *value_str, + int value_pos); +void _dbus_type_writer_init_types_delayed (DBusTypeWriter *writer, + int byte_order, + DBusString *value_str, + int value_pos); +void _dbus_type_writer_add_types (DBusTypeWriter *writer, + DBusString *type_str, + int type_pos); +void _dbus_type_writer_remove_types (DBusTypeWriter *writer); +void _dbus_type_writer_init_values_only (DBusTypeWriter *writer, + int byte_order, + const DBusString *type_str, + int type_pos, + DBusString *value_str, + int value_pos); +dbus_bool_t _dbus_type_writer_write_basic (DBusTypeWriter *writer, + int type, + const void *value); +dbus_bool_t _dbus_type_writer_write_fixed_multi (DBusTypeWriter *writer, + int element_type, + const void *value, + int n_elements); +dbus_bool_t _dbus_type_writer_recurse (DBusTypeWriter *writer, + int container_type, + const DBusString *contained_type, + int contained_type_start, + DBusTypeWriter *sub); +dbus_bool_t _dbus_type_writer_unrecurse (DBusTypeWriter *writer, + DBusTypeWriter *sub); +dbus_bool_t _dbus_type_writer_append_array (DBusTypeWriter *writer, + const DBusString *contained_type, + int contained_type_start, + DBusTypeWriter *sub); +dbus_bool_t _dbus_type_writer_write_reader (DBusTypeWriter *writer, + DBusTypeReader *reader); +dbus_bool_t _dbus_type_writer_write_reader_partial (DBusTypeWriter *writer, + DBusTypeReader *reader, + const DBusTypeReader *start_after, + int start_after_new_pos, + int start_after_new_len, + DBusList **fixups); +void _dbus_type_writer_set_enabled (DBusTypeWriter *writer, + dbus_bool_t enabled); + + +#endif /* DBUS_MARSHAL_RECURSIVE_H */ diff --git a/src/dbus/dbus-marshal-validate-util.c b/src/dbus/dbus-marshal-validate-util.c new file mode 100644 index 0000000..ac901c3 --- /dev/null +++ b/src/dbus/dbus-marshal-validate-util.c @@ -0,0 +1,588 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-marshal-validate-util.c Would be in dbus-marshal-validate.c, but only used by tests/bus + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#ifdef DBUS_BUILD_TESTS + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +#include "dbus-internals.h" +#include "dbus-marshal-validate.h" +#include "dbus-marshal-recursive.h" + +#include "dbus-test.h" +#include + +typedef struct +{ + const char *data; + DBusValidity expected; +} ValidityTest; + +static void +run_validity_tests (const ValidityTest *tests, + int n_tests, + DBusValidity (* func) (const DBusString*,int,int)) +{ + int i; + + for (i = 0; i < n_tests; i++) + { + DBusString str; + DBusValidity v; + + _dbus_string_init_const (&str, tests[i].data); + + v = (*func) (&str, 0, _dbus_string_get_length (&str)); + + if (v != tests[i].expected) + { + _dbus_warn ("Improper validation result %d for '%s'\n", + v, tests[i].data); + _dbus_assert_not_reached ("test failed"); + } + + ++i; + } +} + +static const ValidityTest signature_tests[] = { + { "", DBUS_VALID }, + { "i", DBUS_VALID }, + { "ai", DBUS_VALID }, + { "(i)", DBUS_VALID }, + { "w", DBUS_INVALID_UNKNOWN_TYPECODE }, + { "a", DBUS_INVALID_MISSING_ARRAY_ELEMENT_TYPE }, + { "aaaaaa", DBUS_INVALID_MISSING_ARRAY_ELEMENT_TYPE }, + { "ii(ii)a", DBUS_INVALID_MISSING_ARRAY_ELEMENT_TYPE }, + { "ia", DBUS_INVALID_MISSING_ARRAY_ELEMENT_TYPE }, + /* DBUS_INVALID_SIGNATURE_TOO_LONG, */ /* too hard to test this way */ + { "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + DBUS_INVALID_EXCEEDED_MAXIMUM_ARRAY_RECURSION }, + { "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((ii))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))", + DBUS_INVALID_EXCEEDED_MAXIMUM_STRUCT_RECURSION }, + { ")", DBUS_INVALID_STRUCT_ENDED_BUT_NOT_STARTED }, + { "i)", DBUS_INVALID_STRUCT_ENDED_BUT_NOT_STARTED }, + { "a)", DBUS_INVALID_STRUCT_ENDED_BUT_NOT_STARTED }, + { "(", DBUS_INVALID_STRUCT_STARTED_BUT_NOT_ENDED }, + { "(i", DBUS_INVALID_STRUCT_STARTED_BUT_NOT_ENDED }, + { "(iiiii", DBUS_INVALID_STRUCT_STARTED_BUT_NOT_ENDED }, + { "(ai", DBUS_INVALID_STRUCT_STARTED_BUT_NOT_ENDED }, + { "()", DBUS_INVALID_STRUCT_HAS_NO_FIELDS }, + { "(())", DBUS_INVALID_STRUCT_HAS_NO_FIELDS }, + { "a()", DBUS_INVALID_STRUCT_HAS_NO_FIELDS }, + { "i()", DBUS_INVALID_STRUCT_HAS_NO_FIELDS }, + { "()i", DBUS_INVALID_STRUCT_HAS_NO_FIELDS }, + { "(a)", DBUS_INVALID_MISSING_ARRAY_ELEMENT_TYPE }, + { "a{ia}", DBUS_INVALID_MISSING_ARRAY_ELEMENT_TYPE }, + { "a{}", DBUS_INVALID_DICT_ENTRY_HAS_NO_FIELDS }, + { "a{aii}", DBUS_INVALID_DICT_KEY_MUST_BE_BASIC_TYPE }, + /* { "a{i}", DBUS_INVALID_DICT_ENTRY_HAS_ONLY_ONE_FIELD }, */ + /* { "{is}", DBUS_INVALID_DICT_ENTRY_NOT_INSIDE_ARRAY }, */ + /* { "a{isi}", DBUS_INVALID_DICT_ENTRY_HAS_TOO_MANY_FIELDS }, */ +}; + +dbus_bool_t +_dbus_marshal_validate_test (void) +{ + DBusString str; + int i; + + const char *valid_paths[] = { + "/", + "/foo/bar", + "/foo", + "/foo/bar/baz" + }; + const char *invalid_paths[] = { + "bar", + "bar/baz", + "/foo/bar/", + "/foo/" + "foo/", + "boo//blah", + "//", + "///", + "foo///blah/", + "Hello World", + "", + " ", + "foo bar" + }; + + const char *valid_interfaces[] = { + "org.freedesktop.Foo", + "Bar.Baz", + "Blah.Blah.Blah.Blah.Blah", + "a.b", + "a.b.c.d.e.f.g", + "a0.b1.c2.d3.e4.f5.g6", + "abc123.foo27" + }; + const char *invalid_interfaces[] = { + ".", + "", + "..", + ".Foo.Bar", + "..Foo.Bar", + "Foo.Bar.", + "Foo.Bar..", + "Foo", + "9foo.bar.baz", + "foo.bar..baz", + "foo.bar...baz", + "foo.bar.b..blah", + ":", + ":0-1", + "10", + ":11.34324", + "0.0.0", + "0..0", + "foo.Bar.%", + "foo.Bar!!", + "!Foo.bar.bz", + "foo.$.blah", + "", + " ", + "foo bar" + }; + + const char *valid_unique_names[] = { + ":0", + ":a", + ":", + ":.a", + ":.1", + ":0.1", + ":000.2222", + ":.blah", + ":abce.freedesktop.blah" + }; + const char *invalid_unique_names[] = { + //":-", + ":!", + //":0-10", + ":blah.", + ":blah.", + ":blah..org", + ":blah.org..", + ":..blah.org", + "", + " ", + "foo bar" + }; + + const char *valid_members[] = { + "Hello", + "Bar", + "foobar", + "_foobar", + "foo89" + }; + + const char *invalid_members[] = { + "9Hello", + "10", + "1", + "foo-bar", + "blah.org", + ".blah", + "blah.", + "Hello.", + "!foo", + "", + " ", + "foo bar" + }; + + const char *valid_signatures[] = { + "", + "sss", + "i", + "b" + }; + + const char *invalid_signatures[] = { + " ", + "not a valid signature", + "123", + ".", + "(", + "a{(ii)i}" /* https://bugs.freedesktop.org/show_bug.cgi?id=17803 */ + }; + + /* Signature with reason */ + + run_validity_tests (signature_tests, _DBUS_N_ELEMENTS (signature_tests), + _dbus_validate_signature_with_reason); + + /* Path validation */ + i = 0; + while (i < (int) _DBUS_N_ELEMENTS (valid_paths)) + { + _dbus_string_init_const (&str, valid_paths[i]); + + if (!_dbus_validate_path (&str, 0, + _dbus_string_get_length (&str))) + { + _dbus_warn ("Path \"%s\" should have been valid\n", valid_paths[i]); + _dbus_assert_not_reached ("invalid path"); + } + + ++i; + } + + i = 0; + while (i < (int) _DBUS_N_ELEMENTS (invalid_paths)) + { + _dbus_string_init_const (&str, invalid_paths[i]); + + if (_dbus_validate_path (&str, 0, + _dbus_string_get_length (&str))) + { + _dbus_warn ("Path \"%s\" should have been invalid\n", invalid_paths[i]); + _dbus_assert_not_reached ("valid path"); + } + + ++i; + } + + /* Interface validation */ + i = 0; + while (i < (int) _DBUS_N_ELEMENTS (valid_interfaces)) + { + _dbus_string_init_const (&str, valid_interfaces[i]); + + if (!_dbus_validate_interface (&str, 0, + _dbus_string_get_length (&str))) + { + _dbus_warn ("Interface \"%s\" should have been valid\n", valid_interfaces[i]); + _dbus_assert_not_reached ("invalid interface"); + } + + ++i; + } + + i = 0; + while (i < (int) _DBUS_N_ELEMENTS (invalid_interfaces)) + { + _dbus_string_init_const (&str, invalid_interfaces[i]); + + if (_dbus_validate_interface (&str, 0, + _dbus_string_get_length (&str))) + { + _dbus_warn ("Interface \"%s\" should have been invalid\n", invalid_interfaces[i]); + _dbus_assert_not_reached ("valid interface"); + } + + ++i; + } + + /* Bus name validation (check that valid interfaces are valid bus names, + * and invalid interfaces are invalid services except if they start with ':') + */ + i = 0; + while (i < (int) _DBUS_N_ELEMENTS (valid_interfaces)) + { + _dbus_string_init_const (&str, valid_interfaces[i]); + + if (!_dbus_validate_bus_name (&str, 0, + _dbus_string_get_length (&str))) + { + _dbus_warn ("Bus name \"%s\" should have been valid\n", valid_interfaces[i]); + _dbus_assert_not_reached ("invalid bus name"); + } + + ++i; + } + + i = 0; + while (i < (int) _DBUS_N_ELEMENTS (invalid_interfaces)) + { + if (invalid_interfaces[i][0] != ':') + { + _dbus_string_init_const (&str, invalid_interfaces[i]); + + if (_dbus_validate_bus_name (&str, 0, + _dbus_string_get_length (&str))) + { + _dbus_warn ("Bus name \"%s\" should have been invalid\n", invalid_interfaces[i]); + _dbus_assert_not_reached ("valid bus name"); + } + } + + ++i; + } + + /* unique name validation */ + i = 0; + while (i < (int) _DBUS_N_ELEMENTS (valid_unique_names)) + { + _dbus_string_init_const (&str, valid_unique_names[i]); + + if (!_dbus_validate_bus_name (&str, 0, + _dbus_string_get_length (&str))) + { + _dbus_warn ("Bus name \"%s\" should have been valid\n", valid_unique_names[i]); + _dbus_assert_not_reached ("invalid unique name"); + } + + ++i; + } + + i = 0; + while (i < (int) _DBUS_N_ELEMENTS (invalid_unique_names)) + { + _dbus_string_init_const (&str, invalid_unique_names[i]); + + if (_dbus_validate_bus_name (&str, 0, + _dbus_string_get_length (&str))) + { + _dbus_warn ("Bus name \"%s\" should have been invalid\n", invalid_unique_names[i]); + _dbus_assert_not_reached ("valid unique name"); + } + + ++i; + } + + + /* Error name validation (currently identical to interfaces) + */ + i = 0; + while (i < (int) _DBUS_N_ELEMENTS (valid_interfaces)) + { + _dbus_string_init_const (&str, valid_interfaces[i]); + + if (!_dbus_validate_error_name (&str, 0, + _dbus_string_get_length (&str))) + { + _dbus_warn ("Error name \"%s\" should have been valid\n", valid_interfaces[i]); + _dbus_assert_not_reached ("invalid error name"); + } + + ++i; + } + + i = 0; + while (i < (int) _DBUS_N_ELEMENTS (invalid_interfaces)) + { + if (invalid_interfaces[i][0] != ':') + { + _dbus_string_init_const (&str, invalid_interfaces[i]); + + if (_dbus_validate_error_name (&str, 0, + _dbus_string_get_length (&str))) + { + _dbus_warn ("Error name \"%s\" should have been invalid\n", invalid_interfaces[i]); + _dbus_assert_not_reached ("valid error name"); + } + } + + ++i; + } + + /* Member validation */ + i = 0; + while (i < (int) _DBUS_N_ELEMENTS (valid_members)) + { + _dbus_string_init_const (&str, valid_members[i]); + + if (!_dbus_validate_member (&str, 0, + _dbus_string_get_length (&str))) + { + _dbus_warn ("Member \"%s\" should have been valid\n", valid_members[i]); + _dbus_assert_not_reached ("invalid member"); + } + + ++i; + } + + i = 0; + while (i < (int) _DBUS_N_ELEMENTS (invalid_members)) + { + _dbus_string_init_const (&str, invalid_members[i]); + + if (_dbus_validate_member (&str, 0, + _dbus_string_get_length (&str))) + { + _dbus_warn ("Member \"%s\" should have been invalid\n", invalid_members[i]); + _dbus_assert_not_reached ("valid member"); + } + + ++i; + } + + /* Signature validation */ + i = 0; + while (i < (int) _DBUS_N_ELEMENTS (valid_signatures)) + { + _dbus_string_init_const (&str, valid_signatures[i]); + + if (!_dbus_validate_signature (&str, 0, + _dbus_string_get_length (&str))) + { + _dbus_warn ("Signature \"%s\" should have been valid\n", valid_signatures[i]); + _dbus_assert_not_reached ("invalid signature"); + } + + ++i; + } + + i = 0; + while (i < (int) _DBUS_N_ELEMENTS (invalid_signatures)) + { + _dbus_string_init_const (&str, invalid_signatures[i]); + + if (_dbus_validate_signature (&str, 0, + _dbus_string_get_length (&str))) + { + _dbus_warn ("Signature \"%s\" should have been invalid\n", invalid_signatures[i]); + _dbus_assert_not_reached ("valid signature"); + } + + ++i; + } + + /* Validate claimed length longer than real length */ + _dbus_string_init_const (&str, "abc.efg"); + if (_dbus_validate_bus_name (&str, 0, 8)) + _dbus_assert_not_reached ("validated too-long string"); + if (_dbus_validate_interface (&str, 0, 8)) + _dbus_assert_not_reached ("validated too-long string"); + if (_dbus_validate_error_name (&str, 0, 8)) + _dbus_assert_not_reached ("validated too-long string"); + + _dbus_string_init_const (&str, "abc"); + if (_dbus_validate_member (&str, 0, 4)) + _dbus_assert_not_reached ("validated too-long string"); + + _dbus_string_init_const (&str, "sss"); + if (_dbus_validate_signature (&str, 0, 4)) + _dbus_assert_not_reached ("validated too-long signature"); + + /* Validate string exceeding max name length */ + if (!_dbus_string_init (&str)) + _dbus_assert_not_reached ("no memory"); + + while (_dbus_string_get_length (&str) <= DBUS_MAXIMUM_NAME_LENGTH) + if (!_dbus_string_append (&str, "abc.def")) + _dbus_assert_not_reached ("no memory"); + + if (_dbus_validate_bus_name (&str, 0, _dbus_string_get_length (&str))) + _dbus_assert_not_reached ("validated overmax string"); + if (_dbus_validate_interface (&str, 0, _dbus_string_get_length (&str))) + _dbus_assert_not_reached ("validated overmax string"); + if (_dbus_validate_error_name (&str, 0, _dbus_string_get_length (&str))) + _dbus_assert_not_reached ("validated overmax string"); + + /* overlong member */ + _dbus_string_set_length (&str, 0); + while (_dbus_string_get_length (&str) <= DBUS_MAXIMUM_NAME_LENGTH) + if (!_dbus_string_append (&str, "abc")) + _dbus_assert_not_reached ("no memory"); + + if (_dbus_validate_member (&str, 0, _dbus_string_get_length (&str))) + _dbus_assert_not_reached ("validated overmax string"); + + /* overlong unique name */ + _dbus_string_set_length (&str, 0); + _dbus_string_append (&str, ":"); + while (_dbus_string_get_length (&str) <= DBUS_MAXIMUM_NAME_LENGTH) + if (!_dbus_string_append (&str, "abc")) + _dbus_assert_not_reached ("no memory"); + + if (_dbus_validate_bus_name (&str, 0, _dbus_string_get_length (&str))) + _dbus_assert_not_reached ("validated overmax string"); + + _dbus_string_free (&str); + + /* Body validation; test basic validation of valid bodies for both endian */ + + { + int sequence; + DBusString signature; + DBusString body; + + if (!_dbus_string_init (&signature) || !_dbus_string_init (&body)) + _dbus_assert_not_reached ("oom"); + + sequence = 0; + while (dbus_internal_do_not_use_generate_bodies (sequence, + DBUS_LITTLE_ENDIAN, + &signature, &body)) + { + DBusValidity validity; + + validity = _dbus_validate_body_with_reason (&signature, 0, + DBUS_LITTLE_ENDIAN, + NULL, &body, 0, + _dbus_string_get_length (&body)); + if (validity != DBUS_VALID) + { + _dbus_warn ("invalid code %d expected valid on sequence %d little endian\n", + validity, sequence); + _dbus_verbose_bytes_of_string (&signature, 0, _dbus_string_get_length (&signature)); + _dbus_verbose_bytes_of_string (&body, 0, _dbus_string_get_length (&body)); + _dbus_assert_not_reached ("test failed"); + } + + _dbus_string_set_length (&signature, 0); + _dbus_string_set_length (&body, 0); + ++sequence; + } + + sequence = 0; + while (dbus_internal_do_not_use_generate_bodies (sequence, + DBUS_BIG_ENDIAN, + &signature, &body)) + { + DBusValidity validity; + + validity = _dbus_validate_body_with_reason (&signature, 0, + DBUS_BIG_ENDIAN, + NULL, &body, 0, + _dbus_string_get_length (&body)); + if (validity != DBUS_VALID) + { + _dbus_warn ("invalid code %d expected valid on sequence %d big endian\n", + validity, sequence); + _dbus_verbose_bytes_of_string (&signature, 0, _dbus_string_get_length (&signature)); + _dbus_verbose_bytes_of_string (&body, 0, _dbus_string_get_length (&body)); + _dbus_assert_not_reached ("test failed"); + } + + _dbus_string_set_length (&signature, 0); + _dbus_string_set_length (&body, 0); + ++sequence; + } + + _dbus_string_free (&signature); + _dbus_string_free (&body); + } + + return TRUE; +} + +#endif /* !DOXYGEN_SHOULD_SKIP_THIS */ + +#endif /* DBUS_BUILD_TESTS */ diff --git a/src/dbus/dbus-marshal-validate.c b/src/dbus/dbus-marshal-validate.c new file mode 100644 index 0000000..78d5594 --- /dev/null +++ b/src/dbus/dbus-marshal-validate.c @@ -0,0 +1,1204 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-marshal-validate.c Validation routines for marshaled data + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-internals.h" +#include "dbus-marshal-validate.h" +#include "dbus-marshal-recursive.h" +#include "dbus-marshal-basic.h" +#include "dbus-signature.h" +#include "dbus-string.h" + +/** + * @addtogroup DBusMarshal + * + * @{ + */ + +/** + * Verifies that the range of type_str from type_pos to type_end is a + * valid signature. If this function returns #TRUE, it will be safe + * to iterate over the signature with a types-only #DBusTypeReader. + * The range passed in should NOT include the terminating + * nul/DBUS_TYPE_INVALID. + * + * @param type_str the string + * @param type_pos where the typecodes start + * @param len length of typecodes + * @returns #DBUS_VALID if valid, reason why invalid otherwise + */ +DBusValidity +_dbus_validate_signature_with_reason (const DBusString *type_str, + int type_pos, + int len) +{ + const unsigned char *p; + const unsigned char *end; + int last; + int struct_depth; + int array_depth; + int dict_entry_depth; + DBusValidity result; + + int element_count; + DBusList *element_count_stack; + + result = DBUS_VALID; + element_count_stack = NULL; + + if (!_dbus_list_append (&element_count_stack, _DBUS_INT_TO_POINTER (0))) + { + result = DBUS_VALIDITY_UNKNOWN_OOM_ERROR; + goto out; + } + + _dbus_assert (type_str != NULL); + _dbus_assert (type_pos < _DBUS_INT32_MAX - len); + _dbus_assert (len >= 0); + _dbus_assert (type_pos >= 0); + + if (len > DBUS_MAXIMUM_SIGNATURE_LENGTH) + { + result = DBUS_INVALID_SIGNATURE_TOO_LONG; + goto out; + } + + p = _dbus_string_get_const_data_len (type_str, type_pos, 0); + + end = _dbus_string_get_const_data_len (type_str, type_pos + len, 0); + struct_depth = 0; + array_depth = 0; + dict_entry_depth = 0; + last = DBUS_TYPE_INVALID; + + while (p != end) + { + switch (*p) + { + case DBUS_TYPE_BYTE: + case DBUS_TYPE_BOOLEAN: + case DBUS_TYPE_INT16: + case DBUS_TYPE_UINT16: + case DBUS_TYPE_INT32: + case DBUS_TYPE_UINT32: + case DBUS_TYPE_INT64: + case DBUS_TYPE_UINT64: + case DBUS_TYPE_DOUBLE: + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + case DBUS_TYPE_SIGNATURE: + case DBUS_TYPE_VARIANT: + break; + + case DBUS_TYPE_ARRAY: + array_depth += 1; + if (array_depth > DBUS_MAXIMUM_TYPE_RECURSION_DEPTH) + { + result = DBUS_INVALID_EXCEEDED_MAXIMUM_ARRAY_RECURSION; + goto out; + } + break; + + case DBUS_STRUCT_BEGIN_CHAR: + struct_depth += 1; + + if (struct_depth > DBUS_MAXIMUM_TYPE_RECURSION_DEPTH) + { + result = DBUS_INVALID_EXCEEDED_MAXIMUM_STRUCT_RECURSION; + goto out; + } + + if (!_dbus_list_append (&element_count_stack, + _DBUS_INT_TO_POINTER (0))) + { + result = DBUS_VALIDITY_UNKNOWN_OOM_ERROR; + goto out; + } + + break; + + case DBUS_STRUCT_END_CHAR: + if (struct_depth == 0) + { + result = DBUS_INVALID_STRUCT_ENDED_BUT_NOT_STARTED; + goto out; + } + + if (last == DBUS_STRUCT_BEGIN_CHAR) + { + result = DBUS_INVALID_STRUCT_HAS_NO_FIELDS; + goto out; + } + + _dbus_list_pop_last (&element_count_stack); + + struct_depth -= 1; + break; + + case DBUS_DICT_ENTRY_BEGIN_CHAR: + if (last != DBUS_TYPE_ARRAY) + { + result = DBUS_INVALID_DICT_ENTRY_NOT_INSIDE_ARRAY; + goto out; + } + + dict_entry_depth += 1; + + if (dict_entry_depth > DBUS_MAXIMUM_TYPE_RECURSION_DEPTH) + { + result = DBUS_INVALID_EXCEEDED_MAXIMUM_DICT_ENTRY_RECURSION; + goto out; + } + + if (!_dbus_list_append (&element_count_stack, + _DBUS_INT_TO_POINTER (0))) + { + result = DBUS_VALIDITY_UNKNOWN_OOM_ERROR; + goto out; + } + + break; + + case DBUS_DICT_ENTRY_END_CHAR: + if (dict_entry_depth == 0) + { + result = DBUS_INVALID_DICT_ENTRY_ENDED_BUT_NOT_STARTED; + goto out; + } + + dict_entry_depth -= 1; + + element_count = + _DBUS_POINTER_TO_INT (_dbus_list_pop_last (&element_count_stack)); + + if (element_count != 2) + { + if (element_count == 0) + result = DBUS_INVALID_DICT_ENTRY_HAS_NO_FIELDS; + else if (element_count == 1) + result = DBUS_INVALID_DICT_ENTRY_HAS_ONLY_ONE_FIELD; + else + result = DBUS_INVALID_DICT_ENTRY_HAS_TOO_MANY_FIELDS; + + goto out; + } + break; + + case DBUS_TYPE_STRUCT: /* doesn't appear in signatures */ + case DBUS_TYPE_DICT_ENTRY: /* ditto */ + default: + result = DBUS_INVALID_UNKNOWN_TYPECODE; + goto out; + } + + if (*p != DBUS_TYPE_ARRAY && + *p != DBUS_DICT_ENTRY_BEGIN_CHAR && + *p != DBUS_STRUCT_BEGIN_CHAR) + { + element_count = + _DBUS_POINTER_TO_INT (_dbus_list_pop_last (&element_count_stack)); + + ++element_count; + + if (!_dbus_list_append (&element_count_stack, + _DBUS_INT_TO_POINTER (element_count))) + { + result = DBUS_VALIDITY_UNKNOWN_OOM_ERROR; + goto out; + } + } + + if (array_depth > 0) + { + if (*p == DBUS_TYPE_ARRAY && p != end) + { + const char *p1; + p1 = p + 1; + if (*p1 == DBUS_STRUCT_END_CHAR || + *p1 == DBUS_DICT_ENTRY_END_CHAR) + { + result = DBUS_INVALID_MISSING_ARRAY_ELEMENT_TYPE; + goto out; + } + } + else + { + array_depth = 0; + } + } + + if (last == DBUS_DICT_ENTRY_BEGIN_CHAR) + { + if (!(_dbus_type_is_valid (*p) && dbus_type_is_basic (*p))) + { + result = DBUS_INVALID_DICT_KEY_MUST_BE_BASIC_TYPE; + goto out; + } + } + + last = *p; + ++p; + } + + + if (array_depth > 0) + { + result = DBUS_INVALID_MISSING_ARRAY_ELEMENT_TYPE; + goto out; + } + + if (struct_depth > 0) + { + result = DBUS_INVALID_STRUCT_STARTED_BUT_NOT_ENDED; + goto out; + } + + if (dict_entry_depth > 0) + { + result = DBUS_INVALID_DICT_ENTRY_STARTED_BUT_NOT_ENDED; + goto out; + } + + _dbus_assert (last != DBUS_TYPE_ARRAY); + _dbus_assert (last != DBUS_STRUCT_BEGIN_CHAR); + _dbus_assert (last != DBUS_DICT_ENTRY_BEGIN_CHAR); + + result = DBUS_VALID; + +out: + _dbus_list_clear (&element_count_stack); + return result; +} + +static DBusValidity +validate_body_helper (DBusTypeReader *reader, + int byte_order, + dbus_bool_t walk_reader_to_end, + const unsigned char *p, + const unsigned char *end, + const unsigned char **new_p) +{ + int current_type; + + while ((current_type = _dbus_type_reader_get_current_type (reader)) != DBUS_TYPE_INVALID) + { + const unsigned char *a; + int alignment; + +#if 0 + _dbus_verbose (" validating value of type %s type reader %p type_pos %d p %p end %p %d remain\n", + _dbus_type_to_string (current_type), reader, reader->type_pos, p, end, + (int) (end - p)); +#endif + + /* Guarantee that p has one byte to look at */ + if (p == end) + return DBUS_INVALID_NOT_ENOUGH_DATA; + + switch (current_type) + { + case DBUS_TYPE_BYTE: + ++p; + break; + + case DBUS_TYPE_BOOLEAN: + case DBUS_TYPE_INT16: + case DBUS_TYPE_UINT16: + case DBUS_TYPE_INT32: + case DBUS_TYPE_UINT32: + case DBUS_TYPE_INT64: + case DBUS_TYPE_UINT64: + case DBUS_TYPE_DOUBLE: + alignment = _dbus_type_get_alignment (current_type); + a = _DBUS_ALIGN_ADDRESS (p, alignment); + if (a >= end) + return DBUS_INVALID_NOT_ENOUGH_DATA; + while (p != a) + { + if (*p != '\0') + return DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL; + ++p; + } + + if (current_type == DBUS_TYPE_BOOLEAN) + { + dbus_uint32_t v = _dbus_unpack_uint32 (byte_order, + p); + if (!(v == 0 || v == 1)) + return DBUS_INVALID_BOOLEAN_NOT_ZERO_OR_ONE; + } + + p += alignment; + break; + + case DBUS_TYPE_ARRAY: + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + { + dbus_uint32_t claimed_len; + + a = _DBUS_ALIGN_ADDRESS (p, 4); + if (a + 4 > end) + return DBUS_INVALID_NOT_ENOUGH_DATA; + while (p != a) + { + if (*p != '\0') + return DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL; + ++p; + } + + claimed_len = _dbus_unpack_uint32 (byte_order, p); + p += 4; + + /* p may now be == end */ + _dbus_assert (p <= end); + + if (current_type == DBUS_TYPE_ARRAY) + { + int array_elem_type = _dbus_type_reader_get_element_type (reader); + + if (!_dbus_type_is_valid (array_elem_type)) + { + return DBUS_INVALID_UNKNOWN_TYPECODE; + } + + alignment = _dbus_type_get_alignment (array_elem_type); + + a = _DBUS_ALIGN_ADDRESS (p, alignment); + + /* a may now be == end */ + if (a > end) + return DBUS_INVALID_NOT_ENOUGH_DATA; + + while (p != a) + { + if (*p != '\0') + return DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL; + ++p; + } + } + + if (claimed_len > (unsigned long) (end - p)) + return DBUS_INVALID_LENGTH_OUT_OF_BOUNDS; + + if (current_type == DBUS_TYPE_OBJECT_PATH) + { + DBusString str; + _dbus_string_init_const_len (&str, p, claimed_len); + if (!_dbus_validate_path (&str, 0, + _dbus_string_get_length (&str))) + return DBUS_INVALID_BAD_PATH; + + p += claimed_len; + } + else if (current_type == DBUS_TYPE_STRING) + { + DBusString str; + _dbus_string_init_const_len (&str, p, claimed_len); + if (!_dbus_string_validate_utf8 (&str, 0, + _dbus_string_get_length (&str))) + return DBUS_INVALID_BAD_UTF8_IN_STRING; + + p += claimed_len; + } + else if (current_type == DBUS_TYPE_ARRAY && claimed_len > 0) + { + DBusTypeReader sub; + DBusValidity validity; + const unsigned char *array_end; + int array_elem_type; + + if (claimed_len > DBUS_MAXIMUM_ARRAY_LENGTH) + return DBUS_INVALID_ARRAY_LENGTH_EXCEEDS_MAXIMUM; + + /* Remember that the reader is types only, so we can't + * use it to iterate over elements. It stays the same + * for all elements. + */ + _dbus_type_reader_recurse (reader, &sub); + + array_end = p + claimed_len; + + array_elem_type = _dbus_type_reader_get_element_type (reader); + + /* avoid recursive call to validate_body_helper if this is an array + * of fixed-size elements + */ + if (dbus_type_is_fixed (array_elem_type)) + { + /* bools need to be handled differently, because they can + * have an invalid value + */ + if (array_elem_type == DBUS_TYPE_BOOLEAN) + { + dbus_uint32_t v; + alignment = _dbus_type_get_alignment (array_elem_type); + + while (p < array_end) + { + v = _dbus_unpack_uint32 (byte_order, p); + + if (!(v == 0 || v == 1)) + return DBUS_INVALID_BOOLEAN_NOT_ZERO_OR_ONE; + + p += alignment; + } + } + + else + { + p = array_end; + } + } + + else + { + while (p < array_end) + { + validity = validate_body_helper (&sub, byte_order, FALSE, p, end, &p); + if (validity != DBUS_VALID) + return validity; + } + } + + if (p != array_end) + return DBUS_INVALID_ARRAY_LENGTH_INCORRECT; + } + + /* check nul termination */ + if (current_type != DBUS_TYPE_ARRAY) + { + if (p == end) + return DBUS_INVALID_NOT_ENOUGH_DATA; + + if (*p != '\0') + return DBUS_INVALID_STRING_MISSING_NUL; + ++p; + } + } + break; + + case DBUS_TYPE_SIGNATURE: + { + dbus_uint32_t claimed_len; + DBusString str; + DBusValidity validity; + + claimed_len = *p; + ++p; + + /* 1 is for nul termination */ + if (claimed_len + 1 > (unsigned long) (end - p)) + return DBUS_INVALID_SIGNATURE_LENGTH_OUT_OF_BOUNDS; + + _dbus_string_init_const_len (&str, p, claimed_len); + validity = + _dbus_validate_signature_with_reason (&str, 0, + _dbus_string_get_length (&str)); + + if (validity != DBUS_VALID) + return validity; + + p += claimed_len; + + _dbus_assert (p < end); + if (*p != DBUS_TYPE_INVALID) + return DBUS_INVALID_SIGNATURE_MISSING_NUL; + + ++p; + + _dbus_verbose ("p = %p end = %p claimed_len %u\n", p, end, claimed_len); + } + break; + + case DBUS_TYPE_VARIANT: + { + /* 1 byte sig len, sig typecodes, align to + * contained-type-boundary, values. + */ + + /* In addition to normal signature validation, we need to be sure + * the signature contains only a single (possibly container) type. + */ + dbus_uint32_t claimed_len; + DBusString sig; + DBusTypeReader sub; + DBusValidity validity; + int contained_alignment; + int contained_type; + DBusValidity reason; + + claimed_len = *p; + ++p; + + /* + 1 for nul */ + if (claimed_len + 1 > (unsigned long) (end - p)) + return DBUS_INVALID_VARIANT_SIGNATURE_LENGTH_OUT_OF_BOUNDS; + + _dbus_string_init_const_len (&sig, p, claimed_len); + reason = _dbus_validate_signature_with_reason (&sig, 0, + _dbus_string_get_length (&sig)); + if (!(reason == DBUS_VALID)) + { + if (reason == DBUS_VALIDITY_UNKNOWN_OOM_ERROR) + return reason; + else + return DBUS_INVALID_VARIANT_SIGNATURE_BAD; + } + + p += claimed_len; + + if (*p != DBUS_TYPE_INVALID) + return DBUS_INVALID_VARIANT_SIGNATURE_MISSING_NUL; + ++p; + + contained_type = _dbus_first_type_in_signature (&sig, 0); + if (contained_type == DBUS_TYPE_INVALID) + return DBUS_INVALID_VARIANT_SIGNATURE_EMPTY; + + contained_alignment = _dbus_type_get_alignment (contained_type); + + a = _DBUS_ALIGN_ADDRESS (p, contained_alignment); + if (a > end) + return DBUS_INVALID_NOT_ENOUGH_DATA; + while (p != a) + { + if (*p != '\0') + return DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL; + ++p; + } + + _dbus_type_reader_init_types_only (&sub, &sig, 0); + + _dbus_assert (_dbus_type_reader_get_current_type (&sub) != DBUS_TYPE_INVALID); + + validity = validate_body_helper (&sub, byte_order, FALSE, p, end, &p); + if (validity != DBUS_VALID) + return validity; + + if (_dbus_type_reader_next (&sub)) + return DBUS_INVALID_VARIANT_SIGNATURE_SPECIFIES_MULTIPLE_VALUES; + + _dbus_assert (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_INVALID); + } + break; + + case DBUS_TYPE_DICT_ENTRY: + case DBUS_TYPE_STRUCT: + { + DBusTypeReader sub; + DBusValidity validity; + + a = _DBUS_ALIGN_ADDRESS (p, 8); + if (a > end) + return DBUS_INVALID_NOT_ENOUGH_DATA; + while (p != a) + { + if (*p != '\0') + return DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL; + ++p; + } + + _dbus_type_reader_recurse (reader, &sub); + + validity = validate_body_helper (&sub, byte_order, TRUE, p, end, &p); + if (validity != DBUS_VALID) + return validity; + } + break; + + default: + _dbus_assert_not_reached ("invalid typecode in supposedly-validated signature"); + break; + } + +#if 0 + _dbus_verbose (" validated value of type %s type reader %p type_pos %d p %p end %p %d remain\n", + _dbus_type_to_string (current_type), reader, reader->type_pos, p, end, + (int) (end - p)); +#endif + + if (p > end) + { + _dbus_verbose ("not enough data!!! p = %p end = %p end-p = %d\n", + p, end, (int) (end - p)); + return DBUS_INVALID_NOT_ENOUGH_DATA; + } + + if (walk_reader_to_end) + _dbus_type_reader_next (reader); + else + break; + } + + if (new_p) + *new_p = p; + + return DBUS_VALID; +} + +/** + * Verifies that the range of value_str from value_pos to value_end is + * a legitimate value of type expected_signature. If this function + * returns #TRUE, it will be safe to iterate over the values with + * #DBusTypeReader. The signature is assumed to be already valid. + * + * If bytes_remaining is not #NULL, then leftover bytes will be stored + * there and #DBUS_VALID returned. If it is #NULL, then + * #DBUS_INVALID_TOO_MUCH_DATA will be returned if bytes are left + * over. + * + * @param expected_signature the expected types in the value_str + * @param expected_signature_start where in expected_signature is the signature + * @param byte_order the byte order + * @param bytes_remaining place to store leftover bytes + * @param value_str the string containing the body + * @param value_pos where the values start + * @param len length of values after value_pos + * @returns #DBUS_VALID if valid, reason why invalid otherwise + */ +DBusValidity +_dbus_validate_body_with_reason (const DBusString *expected_signature, + int expected_signature_start, + int byte_order, + int *bytes_remaining, + const DBusString *value_str, + int value_pos, + int len) +{ + DBusTypeReader reader; + const unsigned char *p; + const unsigned char *end; + DBusValidity validity; + + _dbus_assert (len >= 0); + _dbus_assert (value_pos >= 0); + _dbus_assert (value_pos <= _dbus_string_get_length (value_str) - len); + + _dbus_verbose ("validating body from pos %d len %d sig '%s'\n", + value_pos, len, _dbus_string_get_const_data_len (expected_signature, + expected_signature_start, + 0)); + + _dbus_type_reader_init_types_only (&reader, + expected_signature, expected_signature_start); + + p = _dbus_string_get_const_data_len (value_str, value_pos, len); + end = p + len; + + validity = validate_body_helper (&reader, byte_order, TRUE, p, end, &p); + if (validity != DBUS_VALID) + return validity; + + if (bytes_remaining) + { + *bytes_remaining = end - p; + return DBUS_VALID; + } + else if (p < end) + return DBUS_INVALID_TOO_MUCH_DATA; + else + { + _dbus_assert (p == end); + return DBUS_VALID; + } +} + +/** + * Determine wether the given character is valid as the first character + * in a name. + */ +#define VALID_INITIAL_NAME_CHARACTER(c) \ + ( ((c) >= 'A' && (c) <= 'Z') || \ + ((c) >= 'a' && (c) <= 'z') || \ + ((c) == '_') ) + +/** + * Determine wether the given character is valid as a second or later + * character in a name + */ +#define VALID_NAME_CHARACTER(c) \ + ( ((c) >= '0' && (c) <= '9') || \ + ((c) >= 'A' && (c) <= 'Z') || \ + ((c) >= 'a' && (c) <= 'z') || \ + ((c) == '_') ) + +/** + * Checks that the given range of the string is a valid object path + * name in the D-Bus protocol. Part of the validation ensures that + * the object path contains only ASCII. + * + * @todo this is inconsistent with most of DBusString in that + * it allows a start,len range that extends past the string end. + * + * @todo change spec to disallow more things, such as spaces in the + * path name + * + * @param str the string + * @param start first byte index to check + * @param len number of bytes to check + * @returns #TRUE if the byte range exists and is a valid name + */ +dbus_bool_t +_dbus_validate_path (const DBusString *str, + int start, + int len) +{ + const unsigned char *s; + const unsigned char *end; + const unsigned char *last_slash; + + _dbus_assert (start >= 0); + _dbus_assert (len >= 0); + _dbus_assert (start <= _dbus_string_get_length (str)); + + if (len > _dbus_string_get_length (str) - start) + return FALSE; + + if (len == 0) + return FALSE; + + s = _dbus_string_get_const_data (str) + start; + end = s + len; + + if (*s != '/') + return FALSE; + last_slash = s; + ++s; + + while (s != end) + { + if (*s == '/') + { + if ((s - last_slash) < 2) + return FALSE; /* no empty path components allowed */ + + last_slash = s; + } + else + { + if (_DBUS_UNLIKELY (!VALID_NAME_CHARACTER (*s))) + return FALSE; + } + + ++s; + } + + if ((end - last_slash) < 2 && + len > 1) + return FALSE; /* trailing slash not allowed unless the string is "/" */ + + return TRUE; +} + +const char * +_dbus_validity_to_error_message (DBusValidity validity) +{ + switch (validity) + { + case DBUS_VALIDITY_UNKNOWN_OOM_ERROR: return "Out of memory"; + case DBUS_INVALID_FOR_UNKNOWN_REASON: return "Unknown reason"; + case DBUS_VALID_BUT_INCOMPLETE: return "Valid but incomplete"; + case DBUS_VALIDITY_UNKNOWN: return "Validity unknown"; + case DBUS_VALID: return "Valid"; + case DBUS_INVALID_UNKNOWN_TYPECODE: return "Unknown typecode"; + case DBUS_INVALID_MISSING_ARRAY_ELEMENT_TYPE: return "Missing array element type"; + case DBUS_INVALID_SIGNATURE_TOO_LONG: return "Signature is too long"; + case DBUS_INVALID_EXCEEDED_MAXIMUM_ARRAY_RECURSION: return "Exceeded maximum array recursion"; + case DBUS_INVALID_EXCEEDED_MAXIMUM_STRUCT_RECURSION: return "Exceeded maximum struct recursion"; + case DBUS_INVALID_STRUCT_ENDED_BUT_NOT_STARTED: return "Struct ended but not started"; + case DBUS_INVALID_STRUCT_STARTED_BUT_NOT_ENDED: return "Struct started but not ended"; + case DBUS_INVALID_STRUCT_HAS_NO_FIELDS: return "Struct has no fields"; + case DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL: return "Alignment padding not null"; + case DBUS_INVALID_BOOLEAN_NOT_ZERO_OR_ONE: return "Boolean is not zero or one"; + case DBUS_INVALID_NOT_ENOUGH_DATA: return "Not enough data"; + case DBUS_INVALID_TOO_MUCH_DATA: return "Too much data"; + case DBUS_INVALID_BAD_BYTE_ORDER: return "Bad byte order"; + case DBUS_INVALID_BAD_PROTOCOL_VERSION: return "Bad protocol version"; + case DBUS_INVALID_BAD_MESSAGE_TYPE: return "Bad message type"; + case DBUS_INVALID_BAD_SERIAL: return "Bad serial"; + case DBUS_INVALID_INSANE_FIELDS_ARRAY_LENGTH: return "Insane fields array length"; + case DBUS_INVALID_INSANE_BODY_LENGTH: return "Insane body length"; + case DBUS_INVALID_MESSAGE_TOO_LONG: return "Message too long"; + case DBUS_INVALID_HEADER_FIELD_CODE: return "Header field code"; + case DBUS_INVALID_HEADER_FIELD_HAS_WRONG_TYPE: return "Header field has wrong type"; + case DBUS_INVALID_USES_LOCAL_INTERFACE: return "Uses local interface"; + case DBUS_INVALID_USES_LOCAL_PATH: return "Uses local path"; + case DBUS_INVALID_HEADER_FIELD_APPEARS_TWICE: return "Header field appears twice"; + case DBUS_INVALID_BAD_DESTINATION: return "Bad destination"; + case DBUS_INVALID_BAD_INTERFACE: return "Bad interface"; + case DBUS_INVALID_BAD_MEMBER: return "Bad member"; + case DBUS_INVALID_BAD_ERROR_NAME: return "Bad error name"; + case DBUS_INVALID_BAD_SENDER: return "Bad sender"; + case DBUS_INVALID_MISSING_PATH: return "Missing path"; + case DBUS_INVALID_MISSING_INTERFACE: return "Missing interface"; + case DBUS_INVALID_MISSING_MEMBER: return "Missing member"; + case DBUS_INVALID_MISSING_ERROR_NAME: return "Missing error name"; + case DBUS_INVALID_MISSING_REPLY_SERIAL: return "Missing reply serial"; + case DBUS_INVALID_LENGTH_OUT_OF_BOUNDS: return "Length out of bounds"; + case DBUS_INVALID_ARRAY_LENGTH_EXCEEDS_MAXIMUM: return "Array length exceeds maximum"; + case DBUS_INVALID_BAD_PATH: return "Bad path"; + case DBUS_INVALID_SIGNATURE_LENGTH_OUT_OF_BOUNDS: return "Signature length out of bounds"; + case DBUS_INVALID_BAD_UTF8_IN_STRING: return "Bad utf8 in string"; + case DBUS_INVALID_ARRAY_LENGTH_INCORRECT: return "Array length incorrect"; + case DBUS_INVALID_VARIANT_SIGNATURE_LENGTH_OUT_OF_BOUNDS: return "Variant signature length out of bounds"; + case DBUS_INVALID_VARIANT_SIGNATURE_BAD: return "Variant signature bad"; + case DBUS_INVALID_VARIANT_SIGNATURE_EMPTY: return "Variant signature empty"; + case DBUS_INVALID_VARIANT_SIGNATURE_SPECIFIES_MULTIPLE_VALUES: return "Variant signature specifies multiple values"; + case DBUS_INVALID_VARIANT_SIGNATURE_MISSING_NUL: return "Variant signature missing nul"; + case DBUS_INVALID_STRING_MISSING_NUL: return "String missing nul"; + case DBUS_INVALID_SIGNATURE_MISSING_NUL: return "Signature missing nul"; + case DBUS_INVALID_EXCEEDED_MAXIMUM_DICT_ENTRY_RECURSION: return "Exceeded maximum dict entry recursion"; + case DBUS_INVALID_DICT_ENTRY_ENDED_BUT_NOT_STARTED: return "Dict entry ended but not started"; + case DBUS_INVALID_DICT_ENTRY_STARTED_BUT_NOT_ENDED: return "Dict entry started but not ended"; + case DBUS_INVALID_DICT_ENTRY_HAS_NO_FIELDS: return "Dict entry has no fields"; + case DBUS_INVALID_DICT_ENTRY_HAS_ONLY_ONE_FIELD: return "Dict entry has only one field"; + case DBUS_INVALID_DICT_ENTRY_HAS_TOO_MANY_FIELDS: return "Dict entry has too many fields"; + case DBUS_INVALID_DICT_ENTRY_NOT_INSIDE_ARRAY: return "Dict entry not inside array"; + case DBUS_INVALID_DICT_KEY_MUST_BE_BASIC_TYPE: return "Dict key must be basic type"; + + default: + return "Invalid"; + } +} + +/** + * Checks that the given range of the string is a valid interface name + * in the D-Bus protocol. This includes a length restriction and an + * ASCII subset, see the specification. + * + * @todo this is inconsistent with most of DBusString in that + * it allows a start,len range that extends past the string end. + * + * @param str the string + * @param start first byte index to check + * @param len number of bytes to check + * @returns #TRUE if the byte range exists and is a valid name + */ +dbus_bool_t +_dbus_validate_interface (const DBusString *str, + int start, + int len) +{ + const unsigned char *s; + const unsigned char *end; + const unsigned char *iface; + const unsigned char *last_dot; + + _dbus_assert (start >= 0); + _dbus_assert (len >= 0); + _dbus_assert (start <= _dbus_string_get_length (str)); + + if (len > _dbus_string_get_length (str) - start) + return FALSE; + + if (len > DBUS_MAXIMUM_NAME_LENGTH) + return FALSE; + + if (len == 0) + return FALSE; + + last_dot = NULL; + iface = _dbus_string_get_const_data (str) + start; + end = iface + len; + s = iface; + + /* check special cases of first char so it doesn't have to be done + * in the loop. Note we know len > 0 + */ + if (_DBUS_UNLIKELY (*s == '.')) /* disallow starting with a . */ + return FALSE; + else if (_DBUS_UNLIKELY (!VALID_INITIAL_NAME_CHARACTER (*s))) + return FALSE; + else + ++s; + + while (s != end) + { + if (*s == '.') + { + if (_DBUS_UNLIKELY ((s + 1) == end)) + return FALSE; + else if (_DBUS_UNLIKELY (!VALID_INITIAL_NAME_CHARACTER (*(s + 1)))) + return FALSE; + last_dot = s; + ++s; /* we just validated the next char, so skip two */ + } + else if (_DBUS_UNLIKELY (!VALID_NAME_CHARACTER (*s))) + { + return FALSE; + } + + ++s; + } + + if (_DBUS_UNLIKELY (last_dot == NULL)) + return FALSE; + + return TRUE; +} + +/** + * Checks that the given range of the string is a valid member name + * in the D-Bus protocol. This includes a length restriction, etc., + * see the specification. + * + * @todo this is inconsistent with most of DBusString in that + * it allows a start,len range that extends past the string end. + * + * @param str the string + * @param start first byte index to check + * @param len number of bytes to check + * @returns #TRUE if the byte range exists and is a valid name + */ +dbus_bool_t +_dbus_validate_member (const DBusString *str, + int start, + int len) +{ + const unsigned char *s; + const unsigned char *end; + const unsigned char *member; + + _dbus_assert (start >= 0); + _dbus_assert (len >= 0); + _dbus_assert (start <= _dbus_string_get_length (str)); + + if (len > _dbus_string_get_length (str) - start) + return FALSE; + + if (len > DBUS_MAXIMUM_NAME_LENGTH) + return FALSE; + + if (len == 0) + return FALSE; + + member = _dbus_string_get_const_data (str) + start; + end = member + len; + s = member; + + /* check special cases of first char so it doesn't have to be done + * in the loop. Note we know len > 0 + */ + + if (_DBUS_UNLIKELY (!VALID_INITIAL_NAME_CHARACTER (*s))) + return FALSE; + else + ++s; + + while (s != end) + { + if (_DBUS_UNLIKELY (!VALID_NAME_CHARACTER (*s))) + { + return FALSE; + } + + ++s; + } + + return TRUE; +} + +/** + * Checks that the given range of the string is a valid error name + * in the D-Bus protocol. This includes a length restriction, etc., + * see the specification. + * + * @todo this is inconsistent with most of DBusString in that + * it allows a start,len range that extends past the string end. + * + * @param str the string + * @param start first byte index to check + * @param len number of bytes to check + * @returns #TRUE if the byte range exists and is a valid name + */ +dbus_bool_t +_dbus_validate_error_name (const DBusString *str, + int start, + int len) +{ + /* Same restrictions as interface name at the moment */ + return _dbus_validate_interface (str, start, len); +} + +/** + * Determine wether the given character is valid as the first character + * in a bus name. + */ +#define VALID_INITIAL_BUS_NAME_CHARACTER(c) \ + ( ((c) >= 'A' && (c) <= 'Z') || \ + ((c) >= 'a' && (c) <= 'z') || \ + ((c) == '_') || ((c) == '-')) + +/** + * Determine wether the given character is valid as a second or later + * character in a bus name + */ +#define VALID_BUS_NAME_CHARACTER(c) \ + ( ((c) >= '0' && (c) <= '9') || \ + ((c) >= 'A' && (c) <= 'Z') || \ + ((c) >= 'a' && (c) <= 'z') || \ + ((c) == '_') || ((c) == '-')) + +/** + * Checks that the given range of the string is a valid bus name in + * the D-Bus protocol. This includes a length restriction, etc., see + * the specification. + * + * @todo this is inconsistent with most of DBusString in that + * it allows a start,len range that extends past the string end. + * + * @param str the string + * @param start first byte index to check + * @param len number of bytes to check + * @returns #TRUE if the byte range exists and is a valid name + */ +dbus_bool_t +_dbus_validate_bus_name (const DBusString *str, + int start, + int len) +{ + const unsigned char *s; + const unsigned char *end; + const unsigned char *iface; + const unsigned char *last_dot; + + _dbus_assert (start >= 0); + _dbus_assert (len >= 0); + _dbus_assert (start <= _dbus_string_get_length (str)); + + if (len > _dbus_string_get_length (str) - start) + return FALSE; + + if (len > DBUS_MAXIMUM_NAME_LENGTH) + return FALSE; + + if (len == 0) + return FALSE; + + last_dot = NULL; + iface = _dbus_string_get_const_data (str) + start; + end = iface + len; + s = iface; + + /* check special cases of first char so it doesn't have to be done + * in the loop. Note we know len > 0 + */ + if (*s == ':') + { + /* unique name */ + ++s; + while (s != end) + { + if (*s == '.') + { + if (_DBUS_UNLIKELY ((s + 1) == end)) + return FALSE; + if (_DBUS_UNLIKELY (!VALID_BUS_NAME_CHARACTER (*(s + 1)))) + return FALSE; + ++s; /* we just validated the next char, so skip two */ + } + else if (_DBUS_UNLIKELY (!VALID_BUS_NAME_CHARACTER (*s))) + { + return FALSE; + } + + ++s; + } + + return TRUE; + } + else if (_DBUS_UNLIKELY (*s == '.')) /* disallow starting with a . */ + return FALSE; + else if (_DBUS_UNLIKELY (!VALID_INITIAL_BUS_NAME_CHARACTER (*s))) + return FALSE; + else + ++s; + + while (s != end) + { + if (*s == '.') + { + if (_DBUS_UNLIKELY ((s + 1) == end)) + return FALSE; + else if (_DBUS_UNLIKELY (!VALID_INITIAL_BUS_NAME_CHARACTER (*(s + 1)))) + return FALSE; + last_dot = s; + ++s; /* we just validated the next char, so skip two */ + } + else if (_DBUS_UNLIKELY (!VALID_BUS_NAME_CHARACTER (*s))) + { + return FALSE; + } + + ++s; + } + + if (_DBUS_UNLIKELY (last_dot == NULL)) + return FALSE; + + return TRUE; +} + +/** + * Checks that the given range of the string is a valid message type + * signature in the D-Bus protocol. + * + * @todo this is inconsistent with most of DBusString in that + * it allows a start,len range that extends past the string end. + * + * @param str the string + * @param start first byte index to check + * @param len number of bytes to check + * @returns #TRUE if the byte range exists and is a valid signature + */ +dbus_bool_t +_dbus_validate_signature (const DBusString *str, + int start, + int len) +{ + _dbus_assert (start >= 0); + _dbus_assert (start <= _dbus_string_get_length (str)); + _dbus_assert (len >= 0); + + if (len > _dbus_string_get_length (str) - start) + return FALSE; + + return _dbus_validate_signature_with_reason (str, start, len) == DBUS_VALID; +} + +/** define _dbus_check_is_valid_path() */ +DEFINE_DBUS_NAME_CHECK(path) +/** define _dbus_check_is_valid_interface() */ +DEFINE_DBUS_NAME_CHECK(interface) +/** define _dbus_check_is_valid_member() */ +DEFINE_DBUS_NAME_CHECK(member) +/** define _dbus_check_is_valid_error_name() */ +DEFINE_DBUS_NAME_CHECK(error_name) +/** define _dbus_check_is_valid_bus_name() */ +DEFINE_DBUS_NAME_CHECK(bus_name) +/** define _dbus_check_is_valid_signature() */ +DEFINE_DBUS_NAME_CHECK(signature) + +/** @} */ + +/* tests in dbus-marshal-validate-util.c */ diff --git a/src/dbus/dbus-marshal-validate.h b/src/dbus/dbus-marshal-validate.h new file mode 100644 index 0000000..d09acc6 --- /dev/null +++ b/src/dbus/dbus-marshal-validate.h @@ -0,0 +1,203 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-marshal-validate.h Validation routines for marshaled data + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef DBUS_MARSHAL_VALIDATE_H +#define DBUS_MARSHAL_VALIDATE_H + +#include + +#ifndef PACKAGE +#error "config.h not included here" +#endif + +/** + * @addtogroup DBusMarshal + * + * @{ + */ + +/** + * This is used rather than a bool for high visibility + */ +typedef enum +{ + DBUS_VALIDATION_MODE_WE_TRUST_THIS_DATA_ABSOLUTELY, + DBUS_VALIDATION_MODE_DATA_IS_UNTRUSTED +} DBusValidationMode; + +/** + * This is primarily used in unit testing, so we can verify that each + * invalid message is invalid for the expected reasons. Thus we really + * want a distinct enum value for every codepath leaving the validator + * functions. Enum values are specified manually for ease of debugging + * (so you can see the enum value given a printf) + */ +typedef enum +{ +#define _DBUS_NEGATIVE_VALIDITY_COUNT 4 + DBUS_VALIDITY_UNKNOWN_OOM_ERROR = -4, /**< can't determine validity due to OOM */ + DBUS_INVALID_FOR_UNKNOWN_REASON = -3, + DBUS_VALID_BUT_INCOMPLETE = -2, + DBUS_VALIDITY_UNKNOWN = -1, + DBUS_VALID = 0, /**< the data is valid */ + DBUS_INVALID_UNKNOWN_TYPECODE = 1, + DBUS_INVALID_MISSING_ARRAY_ELEMENT_TYPE = 2, + DBUS_INVALID_SIGNATURE_TOO_LONG = 3, /* this one is impossible right now since + * you can't put a too-long value in a byte + */ + DBUS_INVALID_EXCEEDED_MAXIMUM_ARRAY_RECURSION = 4, + DBUS_INVALID_EXCEEDED_MAXIMUM_STRUCT_RECURSION = 5, + DBUS_INVALID_STRUCT_ENDED_BUT_NOT_STARTED = 6, + DBUS_INVALID_STRUCT_STARTED_BUT_NOT_ENDED = 7, + DBUS_INVALID_STRUCT_HAS_NO_FIELDS = 8, + DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL = 9, + DBUS_INVALID_BOOLEAN_NOT_ZERO_OR_ONE = 10, + DBUS_INVALID_NOT_ENOUGH_DATA = 11, + DBUS_INVALID_TOO_MUCH_DATA = 12, /**< trailing junk makes it invalid */ + DBUS_INVALID_BAD_BYTE_ORDER = 13, + DBUS_INVALID_BAD_PROTOCOL_VERSION = 14, + DBUS_INVALID_BAD_MESSAGE_TYPE = 15, + DBUS_INVALID_BAD_SERIAL = 16, + DBUS_INVALID_INSANE_FIELDS_ARRAY_LENGTH = 17, + DBUS_INVALID_INSANE_BODY_LENGTH = 18, + DBUS_INVALID_MESSAGE_TOO_LONG = 19, + DBUS_INVALID_HEADER_FIELD_CODE = 20, + DBUS_INVALID_HEADER_FIELD_HAS_WRONG_TYPE = 21, + DBUS_INVALID_USES_LOCAL_INTERFACE = 22, + DBUS_INVALID_USES_LOCAL_PATH = 23, + DBUS_INVALID_HEADER_FIELD_APPEARS_TWICE = 24, + DBUS_INVALID_BAD_DESTINATION = 25, + DBUS_INVALID_BAD_INTERFACE = 26, + DBUS_INVALID_BAD_MEMBER = 27, + DBUS_INVALID_BAD_ERROR_NAME = 28, + DBUS_INVALID_BAD_SENDER = 29, + DBUS_INVALID_MISSING_PATH = 30, + DBUS_INVALID_MISSING_INTERFACE = 31, + DBUS_INVALID_MISSING_MEMBER = 32, + DBUS_INVALID_MISSING_ERROR_NAME = 33, + DBUS_INVALID_MISSING_REPLY_SERIAL = 34, + DBUS_INVALID_LENGTH_OUT_OF_BOUNDS = 35, + DBUS_INVALID_ARRAY_LENGTH_EXCEEDS_MAXIMUM = 36, + DBUS_INVALID_BAD_PATH = 37, + DBUS_INVALID_SIGNATURE_LENGTH_OUT_OF_BOUNDS = 38, + DBUS_INVALID_BAD_UTF8_IN_STRING = 39, + DBUS_INVALID_ARRAY_LENGTH_INCORRECT = 40, + DBUS_INVALID_VARIANT_SIGNATURE_LENGTH_OUT_OF_BOUNDS = 41, + DBUS_INVALID_VARIANT_SIGNATURE_BAD = 42, + DBUS_INVALID_VARIANT_SIGNATURE_EMPTY = 43, + DBUS_INVALID_VARIANT_SIGNATURE_SPECIFIES_MULTIPLE_VALUES = 44, + DBUS_INVALID_VARIANT_SIGNATURE_MISSING_NUL = 45, + DBUS_INVALID_STRING_MISSING_NUL = 46, + DBUS_INVALID_SIGNATURE_MISSING_NUL = 47, + DBUS_INVALID_EXCEEDED_MAXIMUM_DICT_ENTRY_RECURSION = 48, + DBUS_INVALID_DICT_ENTRY_ENDED_BUT_NOT_STARTED = 49, + DBUS_INVALID_DICT_ENTRY_STARTED_BUT_NOT_ENDED = 50, + DBUS_INVALID_DICT_ENTRY_HAS_NO_FIELDS = 51, + DBUS_INVALID_DICT_ENTRY_HAS_ONLY_ONE_FIELD = 52, + DBUS_INVALID_DICT_ENTRY_HAS_TOO_MANY_FIELDS = 53, + DBUS_INVALID_DICT_ENTRY_NOT_INSIDE_ARRAY = 54, + DBUS_INVALID_DICT_KEY_MUST_BE_BASIC_TYPE = 55, + DBUS_VALIDITY_LAST +} DBusValidity; + +DBusValidity _dbus_validate_signature_with_reason (const DBusString *type_str, + int type_pos, + int len); +DBusValidity _dbus_validate_body_with_reason (const DBusString *expected_signature, + int expected_signature_start, + int byte_order, + int *bytes_remaining, + const DBusString *value_str, + int value_pos, + int len); + +const char *_dbus_validity_to_error_message (DBusValidity validity); + +dbus_bool_t _dbus_validate_path (const DBusString *str, + int start, + int len); +dbus_bool_t _dbus_validate_interface (const DBusString *str, + int start, + int len); +dbus_bool_t _dbus_validate_member (const DBusString *str, + int start, + int len); +dbus_bool_t _dbus_validate_error_name (const DBusString *str, + int start, + int len); +dbus_bool_t _dbus_validate_bus_name (const DBusString *str, + int start, + int len); +dbus_bool_t _dbus_validate_signature (const DBusString *str, + int start, + int len); + +#ifdef DBUS_DISABLE_CHECKS + +/* Be sure they don't exist, since we don't want to use them outside of checks + * and so we want the compile failure. + */ +#define DECLARE_DBUS_NAME_CHECK(what) +#define DEFINE_DBUS_NAME_CHECK(what) + +#else /* !DBUS_DISABLE_CHECKS */ + +/** A name check is used in _dbus_return_if_fail(), it's not suitable + * for validating untrusted data. use _dbus_validate_whatever for that. + */ +#define DECLARE_DBUS_NAME_CHECK(what) \ +dbus_bool_t _dbus_check_is_valid_##what (const char *name) + +/** Define a name check to be used in _dbus_return_if_fail() statements. + */ +#define DEFINE_DBUS_NAME_CHECK(what) \ +dbus_bool_t \ +_dbus_check_is_valid_##what (const char *name) \ +{ \ + DBusString str; \ + \ + if (name == NULL) \ + return FALSE; \ + \ + _dbus_string_init_const (&str, name); \ + return _dbus_validate_##what (&str, 0, \ + _dbus_string_get_length (&str)); \ +} +#endif /* !DBUS_DISABLE_CHECKS */ + +/** defines _dbus_check_is_valid_path() */ +DECLARE_DBUS_NAME_CHECK(path); +/** defines _dbus_check_is_valid_interface() */ +DECLARE_DBUS_NAME_CHECK(interface); +/** defines _dbus_check_is_valid_member() */ +DECLARE_DBUS_NAME_CHECK(member); +/** defines _dbus_check_is_valid_error_name() */ +DECLARE_DBUS_NAME_CHECK(error_name); +/** defines _dbus_check_is_valid_bus_name() */ +DECLARE_DBUS_NAME_CHECK(bus_name); +/** defines _dbus_check_is_valid_signature() */ +DECLARE_DBUS_NAME_CHECK(signature); + +/** @} */ + +#endif /* DBUS_MARSHAL_VALIDATE_H */ diff --git a/src/dbus/dbus-memory.c b/src/dbus/dbus-memory.c new file mode 100644 index 0000000..8dc9147 --- /dev/null +++ b/src/dbus/dbus-memory.c @@ -0,0 +1,842 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-memory.c D-Bus memory handling + * + * Copyright (C) 2002, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-memory.h" +#include "dbus-internals.h" +#include "dbus-sysdeps.h" +#include "dbus-list.h" +#include + +/** + * @defgroup DBusMemory Memory Allocation + * @ingroup DBus + * @brief dbus_malloc(), dbus_free(), etc. + * + * Functions and macros related to allocating and releasing + * blocks of memory. + * + */ + +/** + * @defgroup DBusMemoryInternals Memory allocation implementation details + * @ingroup DBusInternals + * @brief internals of dbus_malloc() etc. + * + * Implementation details related to allocating and releasing blocks + * of memory. + */ + +/** + * @addtogroup DBusMemory + * + * @{ + */ + +/** + * @def dbus_new + * + * Safe macro for using dbus_malloc(). Accepts the type + * to allocate and the number of type instances to + * allocate as arguments, and returns a memory block + * cast to the desired type, instead of as a void*. + * + * @param type type name to allocate + * @param count number of instances in the allocated array + * @returns the new memory block or #NULL on failure + */ + +/** + * @def dbus_new0 + * + * Safe macro for using dbus_malloc0(). Accepts the type + * to allocate and the number of type instances to + * allocate as arguments, and returns a memory block + * cast to the desired type, instead of as a void*. + * The allocated array is initialized to all-bits-zero. + * + * @param type type name to allocate + * @param count number of instances in the allocated array + * @returns the new memory block or #NULL on failure + */ + +/** + * @typedef DBusFreeFunction + * + * The type of a function which frees a block of memory. + * + * @param memory the memory to free + */ + +/** @} */ /* end of public API docs */ + +/** + * @addtogroup DBusMemoryInternals + * + * @{ + */ + +#ifdef DBUS_BUILD_TESTS +static dbus_bool_t debug_initialized = FALSE; +static int fail_nth = -1; +static size_t fail_size = 0; +static int fail_alloc_counter = _DBUS_INT_MAX; +static int n_failures_per_failure = 1; +static int n_failures_this_failure = 0; +static dbus_bool_t guards = FALSE; +static dbus_bool_t disable_mem_pools = FALSE; +static dbus_bool_t backtrace_on_fail_alloc = FALSE; +static DBusAtomic n_blocks_outstanding = {0}; + +/** value stored in guard padding for debugging buffer overrun */ +#define GUARD_VALUE 0xdeadbeef +/** size of the information about the block stored in guard mode */ +#define GUARD_INFO_SIZE 8 +/** size of the GUARD_VALUE-filled padding after the header info */ +#define GUARD_START_PAD 16 +/** size of the GUARD_VALUE-filled padding at the end of the block */ +#define GUARD_END_PAD 16 +/** size of stuff at start of block */ +#define GUARD_START_OFFSET (GUARD_START_PAD + GUARD_INFO_SIZE) +/** total extra size over the requested allocation for guard stuff */ +#define GUARD_EXTRA_SIZE (GUARD_START_OFFSET + GUARD_END_PAD) + +static void +_dbus_initialize_malloc_debug (void) +{ + if (!debug_initialized) + { + debug_initialized = TRUE; + + if (_dbus_getenv ("DBUS_MALLOC_FAIL_NTH") != NULL) + { + fail_nth = atoi (_dbus_getenv ("DBUS_MALLOC_FAIL_NTH")); + fail_alloc_counter = fail_nth; + _dbus_verbose ("Will fail malloc every %d times\n", fail_nth); + } + + if (_dbus_getenv ("DBUS_MALLOC_FAIL_GREATER_THAN") != NULL) + { + fail_size = atoi (_dbus_getenv ("DBUS_MALLOC_FAIL_GREATER_THAN")); + _dbus_verbose ("Will fail mallocs over %ld bytes\n", + (long) fail_size); + } + + if (_dbus_getenv ("DBUS_MALLOC_GUARDS") != NULL) + { + guards = TRUE; + _dbus_verbose ("Will use malloc guards\n"); + } + + if (_dbus_getenv ("DBUS_DISABLE_MEM_POOLS") != NULL) + { + disable_mem_pools = TRUE; + _dbus_verbose ("Will disable memory pools\n"); + } + + if (_dbus_getenv ("DBUS_MALLOC_BACKTRACES") != NULL) + { + backtrace_on_fail_alloc = TRUE; + _dbus_verbose ("Will backtrace on failing a malloc\n"); + } + } +} + +/** + * Whether to turn off mem pools, useful for leak checking. + * + * @returns #TRUE if mempools should not be used. + */ +dbus_bool_t +_dbus_disable_mem_pools (void) +{ + _dbus_initialize_malloc_debug (); + return disable_mem_pools; +} + +/** + * Sets the number of allocations until we simulate a failed + * allocation. If set to 0, the next allocation to run + * fails; if set to 1, one succeeds then the next fails; etc. + * Set to _DBUS_INT_MAX to not fail anything. + * + * @param until_next_fail number of successful allocs before one fails + */ +void +_dbus_set_fail_alloc_counter (int until_next_fail) +{ + _dbus_initialize_malloc_debug (); + + fail_alloc_counter = until_next_fail; + +#if 0 + _dbus_verbose ("Set fail alloc counter = %d\n", fail_alloc_counter); +#endif +} + +/** + * Gets the number of successful allocs until we'll simulate + * a failed alloc. + * + * @returns current counter value + */ +int +_dbus_get_fail_alloc_counter (void) +{ + _dbus_initialize_malloc_debug (); + + return fail_alloc_counter; +} + +/** + * Sets how many mallocs to fail when the fail alloc counter reaches + * 0. + * + * @param failures_per_failure number to fail + */ +void +_dbus_set_fail_alloc_failures (int failures_per_failure) +{ + n_failures_per_failure = failures_per_failure; +} + +/** + * Gets the number of failures we'll have when the fail malloc + * counter reaches 0. + * + * @returns number of failures planned + */ +int +_dbus_get_fail_alloc_failures (void) +{ + return n_failures_per_failure; +} + +#ifdef DBUS_BUILD_TESTS +/** + * Called when about to alloc some memory; if + * it returns #TRUE, then the allocation should + * fail. If it returns #FALSE, then the allocation + * should not fail. + * + * @returns #TRUE if this alloc should fail + */ +dbus_bool_t +_dbus_decrement_fail_alloc_counter (void) +{ + _dbus_initialize_malloc_debug (); + + if (fail_alloc_counter <= 0) + { + if (backtrace_on_fail_alloc) + _dbus_print_backtrace (); + + _dbus_verbose ("failure %d\n", n_failures_this_failure); + + n_failures_this_failure += 1; + if (n_failures_this_failure >= n_failures_per_failure) + { + if (fail_nth >= 0) + fail_alloc_counter = fail_nth; + else + fail_alloc_counter = _DBUS_INT_MAX; + + n_failures_this_failure = 0; + + _dbus_verbose ("reset fail alloc counter to %d\n", fail_alloc_counter); + } + + return TRUE; + } + else + { + fail_alloc_counter -= 1; + return FALSE; + } +} +#endif /* DBUS_BUILD_TESTS */ + +/** + * Get the number of outstanding malloc()'d blocks. + * + * @returns number of blocks + */ +int +_dbus_get_malloc_blocks_outstanding (void) +{ + return n_blocks_outstanding.value; +} + +/** + * Where the block came from. + */ +typedef enum +{ + SOURCE_UNKNOWN, + SOURCE_MALLOC, + SOURCE_REALLOC, + SOURCE_MALLOC_ZERO, + SOURCE_REALLOC_NULL +} BlockSource; + +static const char* +source_string (BlockSource source) +{ + switch (source) + { + case SOURCE_UNKNOWN: + return "unknown"; + case SOURCE_MALLOC: + return "malloc"; + case SOURCE_REALLOC: + return "realloc"; + case SOURCE_MALLOC_ZERO: + return "malloc0"; + case SOURCE_REALLOC_NULL: + return "realloc(NULL)"; + } + _dbus_assert_not_reached ("Invalid malloc block source ID"); + return "invalid!"; +} + +static void +check_guards (void *free_block, + dbus_bool_t overwrite) +{ + if (free_block != NULL) + { + unsigned char *block = ((unsigned char*)free_block) - GUARD_START_OFFSET; + size_t requested_bytes = *(dbus_uint32_t*)block; + BlockSource source = *(dbus_uint32_t*)(block + 4); + unsigned int i; + dbus_bool_t failed; + + failed = FALSE; + +#if 0 + _dbus_verbose ("Checking %d bytes request from source %s\n", + requested_bytes, source_string (source)); +#endif + + i = GUARD_INFO_SIZE; + while (i < GUARD_START_OFFSET) + { + dbus_uint32_t value = *(dbus_uint32_t*) &block[i]; + if (value != GUARD_VALUE) + { + _dbus_warn ("Block of %lu bytes from %s had start guard value 0x%ux at %d expected 0x%x\n", + (long) requested_bytes, source_string (source), + value, i, GUARD_VALUE); + failed = TRUE; + } + + i += 4; + } + + i = GUARD_START_OFFSET + requested_bytes; + while (i < (GUARD_START_OFFSET + requested_bytes + GUARD_END_PAD)) + { + dbus_uint32_t value = *(dbus_uint32_t*) &block[i]; + if (value != GUARD_VALUE) + { + _dbus_warn ("Block of %lu bytes from %s had end guard value 0x%ux at %d expected 0x%x\n", + (long) requested_bytes, source_string (source), + value, i, GUARD_VALUE); + failed = TRUE; + } + + i += 4; + } + + /* set memory to anything but nul bytes */ + if (overwrite) + memset (free_block, 'g', requested_bytes); + + if (failed) + _dbus_assert_not_reached ("guard value corruption"); + } +} + +static void* +set_guards (void *real_block, + size_t requested_bytes, + BlockSource source) +{ + unsigned char *block = real_block; + unsigned int i; + + if (block == NULL) + return NULL; + + _dbus_assert (GUARD_START_OFFSET + GUARD_END_PAD == GUARD_EXTRA_SIZE); + + *((dbus_uint32_t*)block) = requested_bytes; + *((dbus_uint32_t*)(block + 4)) = source; + + i = GUARD_INFO_SIZE; + while (i < GUARD_START_OFFSET) + { + (*(dbus_uint32_t*) &block[i]) = GUARD_VALUE; + + i += 4; + } + + i = GUARD_START_OFFSET + requested_bytes; + while (i < (GUARD_START_OFFSET + requested_bytes + GUARD_END_PAD)) + { + (*(dbus_uint32_t*) &block[i]) = GUARD_VALUE; + + i += 4; + } + + check_guards (block + GUARD_START_OFFSET, FALSE); + + return block + GUARD_START_OFFSET; +} + +#endif + +/** @} */ /* End of internals docs */ + + +/** + * @addtogroup DBusMemory + * + * @{ + */ + +/** + * Allocates the given number of bytes, as with standard + * malloc(). Guaranteed to return #NULL if bytes is zero + * on all platforms. Returns #NULL if the allocation fails. + * The memory must be released with dbus_free(). + * + * dbus_malloc() memory is NOT safe to free with regular free() from + * the C library. Free it with dbus_free() only. + * + * @param bytes number of bytes to allocate + * @return allocated memory, or #NULL if the allocation fails. + */ +void* +dbus_malloc (size_t bytes) +{ +#ifdef DBUS_BUILD_TESTS + _dbus_initialize_malloc_debug (); + + if (_dbus_decrement_fail_alloc_counter ()) + { + _dbus_verbose (" FAILING malloc of %ld bytes\n", (long) bytes); + return NULL; + } +#endif + + if (bytes == 0) /* some system mallocs handle this, some don't */ + return NULL; +#ifdef DBUS_BUILD_TESTS + else if (fail_size != 0 && bytes > fail_size) + return NULL; + else if (guards) + { + void *block; + + block = malloc (bytes + GUARD_EXTRA_SIZE); + if (block) + _dbus_atomic_inc (&n_blocks_outstanding); + + return set_guards (block, bytes, SOURCE_MALLOC); + } +#endif + else + { + void *mem; + mem = malloc (bytes); +#ifdef DBUS_BUILD_TESTS + if (mem) + _dbus_atomic_inc (&n_blocks_outstanding); +#endif + return mem; + } +} + +/** + * Allocates the given number of bytes, as with standard malloc(), but + * all bytes are initialized to zero as with calloc(). Guaranteed to + * return #NULL if bytes is zero on all platforms. Returns #NULL if the + * allocation fails. The memory must be released with dbus_free(). + * + * dbus_malloc0() memory is NOT safe to free with regular free() from + * the C library. Free it with dbus_free() only. + * + * @param bytes number of bytes to allocate + * @return allocated memory, or #NULL if the allocation fails. + */ +void* +dbus_malloc0 (size_t bytes) +{ +#ifdef DBUS_BUILD_TESTS + _dbus_initialize_malloc_debug (); + + if (_dbus_decrement_fail_alloc_counter ()) + { + _dbus_verbose (" FAILING malloc0 of %ld bytes\n", (long) bytes); + + return NULL; + } +#endif + + if (bytes == 0) + return NULL; +#ifdef DBUS_BUILD_TESTS + else if (fail_size != 0 && bytes > fail_size) + return NULL; + else if (guards) + { + void *block; + + block = calloc (bytes + GUARD_EXTRA_SIZE, 1); + if (block) + _dbus_atomic_inc (&n_blocks_outstanding); + return set_guards (block, bytes, SOURCE_MALLOC_ZERO); + } +#endif + else + { + void *mem; + mem = calloc (bytes, 1); +#ifdef DBUS_BUILD_TESTS + if (mem) + _dbus_atomic_inc (&n_blocks_outstanding); +#endif + return mem; + } +} + +/** + * Resizes a block of memory previously allocated by dbus_malloc() or + * dbus_malloc0(). Guaranteed to free the memory and return #NULL if bytes + * is zero on all platforms. Returns #NULL if the resize fails. + * If the resize fails, the memory is not freed. + * + * @param memory block to be resized + * @param bytes new size of the memory block + * @return allocated memory, or #NULL if the resize fails. + */ +void* +dbus_realloc (void *memory, + size_t bytes) +{ +#ifdef DBUS_BUILD_TESTS + _dbus_initialize_malloc_debug (); + + if (_dbus_decrement_fail_alloc_counter ()) + { + _dbus_verbose (" FAILING realloc of %ld bytes\n", (long) bytes); + + return NULL; + } +#endif + + if (bytes == 0) /* guarantee this is safe */ + { + dbus_free (memory); + return NULL; + } +#ifdef DBUS_BUILD_TESTS + else if (fail_size != 0 && bytes > fail_size) + return NULL; + else if (guards) + { + if (memory) + { + size_t old_bytes; + void *block; + + check_guards (memory, FALSE); + + block = realloc (((unsigned char*)memory) - GUARD_START_OFFSET, + bytes + GUARD_EXTRA_SIZE); + + old_bytes = *(dbus_uint32_t*)block; + if (block && bytes >= old_bytes) + /* old guards shouldn't have moved */ + check_guards (((unsigned char*)block) + GUARD_START_OFFSET, FALSE); + + return set_guards (block, bytes, SOURCE_REALLOC); + } + else + { + void *block; + + block = malloc (bytes + GUARD_EXTRA_SIZE); + + if (block) + _dbus_atomic_inc (&n_blocks_outstanding); + + return set_guards (block, bytes, SOURCE_REALLOC_NULL); + } + } +#endif + else + { + void *mem; + mem = realloc (memory, bytes); +#ifdef DBUS_BUILD_TESTS + if (memory == NULL && mem != NULL) + _dbus_atomic_inc (&n_blocks_outstanding); +#endif + return mem; + } +} + +/** + * Frees a block of memory previously allocated by dbus_malloc() or + * dbus_malloc0(). If passed #NULL, does nothing. + * + * @param memory block to be freed + */ +void +dbus_free (void *memory) +{ +#ifdef DBUS_BUILD_TESTS + if (guards) + { + check_guards (memory, TRUE); + if (memory) + { + _dbus_atomic_dec (&n_blocks_outstanding); + + _dbus_assert (n_blocks_outstanding.value >= 0); + + free (((unsigned char*)memory) - GUARD_START_OFFSET); + } + + return; + } +#endif + + if (memory) /* we guarantee it's safe to free (NULL) */ + { +#ifdef DBUS_BUILD_TESTS + _dbus_atomic_dec (&n_blocks_outstanding); + + _dbus_assert (n_blocks_outstanding.value >= 0); +#endif + + free (memory); + } +} + +/** + * Frees a #NULL-terminated array of strings. + * If passed #NULL, does nothing. + * + * @param str_array the array to be freed + */ +void +dbus_free_string_array (char **str_array) +{ + if (str_array) + { + int i; + + i = 0; + while (str_array[i]) + { + dbus_free (str_array[i]); + i++; + } + + dbus_free (str_array); + } +} + +/** @} */ /* End of public API docs block */ + + +/** + * @addtogroup DBusMemoryInternals + * + * @{ + */ + +/** + * _dbus_current_generation is used to track each + * time that dbus_shutdown() is called, so we can + * reinit things after it's been called. It is simply + * incremented each time we shut down. + */ +int _dbus_current_generation = 1; + +/** + * Represents a function to be called on shutdown. + */ +typedef struct ShutdownClosure ShutdownClosure; + +/** + * This struct represents a function to be called on shutdown. + */ +struct ShutdownClosure +{ + ShutdownClosure *next; /**< Next ShutdownClosure */ + DBusShutdownFunction func; /**< Function to call */ + void *data; /**< Data for function */ +}; + +_DBUS_DEFINE_GLOBAL_LOCK (shutdown_funcs); +static ShutdownClosure *registered_globals = NULL; + +/** + * Register a cleanup function to be called exactly once + * the next time dbus_shutdown() is called. + * + * @param func the function + * @param data data to pass to the function + * @returns #FALSE on not enough memory + */ +dbus_bool_t +_dbus_register_shutdown_func (DBusShutdownFunction func, + void *data) +{ + ShutdownClosure *c; + + c = dbus_new (ShutdownClosure, 1); + + if (c == NULL) + return FALSE; + + c->func = func; + c->data = data; + + _DBUS_LOCK (shutdown_funcs); + + c->next = registered_globals; + registered_globals = c; + + _DBUS_UNLOCK (shutdown_funcs); + + return TRUE; +} + +/** @} */ /* End of private API docs block */ + + +/** + * @addtogroup DBusMemory + * + * @{ + */ + +/** + * Frees all memory allocated internally by libdbus and + * reverses the effects of dbus_threads_init(). libdbus keeps internal + * global variables, for example caches and thread locks, and it + * can be useful to free these internal data structures. + * + * dbus_shutdown() does NOT free memory that was returned + * to the application. It only returns libdbus-internal + * data structures. + * + * You MUST free all memory and release all reference counts + * returned to you by libdbus prior to calling dbus_shutdown(). + * + * You can't continue to use any D-Bus objects, such as connections, + * that were allocated prior to dbus_shutdown(). You can, however, + * start over; call dbus_threads_init() again, create new connections, + * and so forth. + * + * WARNING: dbus_shutdown() is NOT thread safe, it must be called + * while NO other threads are using D-Bus. (Remember, you have to free + * all D-Bus objects and memory before you call dbus_shutdown(), so no + * thread can be using libdbus.) + * + * The purpose of dbus_shutdown() is to allow applications to get + * clean output from memory leak checkers. dbus_shutdown() may also be + * useful if you want to dlopen() libdbus instead of linking to it, + * and want to be able to unload the library again. + * + * There is absolutely no requirement to call dbus_shutdown() - in fact, + * most applications won't bother and should not feel guilty. + * + * You have to know that nobody is using libdbus in your application's + * process before you can call dbus_shutdown(). One implication of this + * is that calling dbus_shutdown() from a library is almost certainly + * wrong, since you don't know what the rest of the app is up to. + * + */ +void +dbus_shutdown (void) +{ + while (registered_globals != NULL) + { + ShutdownClosure *c; + + c = registered_globals; + registered_globals = c->next; + + (* c->func) (c->data); + + dbus_free (c); + } + + _dbus_current_generation += 1; +} + +/** @} */ /** End of public API docs block */ + +#ifdef DBUS_BUILD_TESTS +#include "dbus-test.h" + +/** + * @ingroup DBusMemoryInternals + * Unit test for DBusMemory + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_memory_test (void) +{ + dbus_bool_t old_guards; + void *p; + size_t size; + + old_guards = guards; + guards = TRUE; + p = dbus_malloc (4); + if (p == NULL) + _dbus_assert_not_reached ("no memory"); + for (size = 4; size < 256; size += 4) + { + p = dbus_realloc (p, size); + if (p == NULL) + _dbus_assert_not_reached ("no memory"); + } + for (size = 256; size != 0; size -= 4) + { + p = dbus_realloc (p, size); + if (p == NULL) + _dbus_assert_not_reached ("no memory"); + } + dbus_free (p); + guards = old_guards; + return TRUE; +} + +#endif diff --git a/src/dbus/dbus-memory.h b/src/dbus/dbus-memory.h new file mode 100644 index 0000000..6aab4e2 --- /dev/null +++ b/src/dbus/dbus-memory.h @@ -0,0 +1,59 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-memory.h D-Bus memory handling + * + * Copyright (C) 2002 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION) +#error "Only can be included directly, this file may disappear or change contents." +#endif + +#ifndef DBUS_MEMORY_H +#define DBUS_MEMORY_H + +#include +#include + +DBUS_BEGIN_DECLS + +/** + * @addtogroup DBusMemory + * @{ + */ + +void* dbus_malloc (size_t bytes); +void* dbus_malloc0 (size_t bytes); +void* dbus_realloc (void *memory, + size_t bytes); +void dbus_free (void *memory); + +#define dbus_new(type, count) ((type*)dbus_malloc (sizeof (type) * (count))); +#define dbus_new0(type, count) ((type*)dbus_malloc0 (sizeof (type) * (count))); + +void dbus_free_string_array (char **str_array); + +typedef void (* DBusFreeFunction) (void *memory); + +void dbus_shutdown (void); + +/** @} */ + +DBUS_END_DECLS + +#endif /* DBUS_MEMORY_H */ diff --git a/src/dbus/dbus-mempool.c b/src/dbus/dbus-mempool.c new file mode 100644 index 0000000..f94134d --- /dev/null +++ b/src/dbus/dbus-mempool.c @@ -0,0 +1,577 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-mempool.h Memory pools + * + * Copyright (C) 2002, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-mempool.h" +#include "dbus-internals.h" + +/** + * @defgroup DBusMemPool memory pools + * @ingroup DBusInternals + * @brief DBusMemPool object + * + * Types and functions related to DBusMemPool. A memory pool is used + * to decrease memory fragmentation/overhead and increase speed for + * blocks of small uniformly-sized objects. The main point is to avoid + * the overhead of a malloc block for each small object, speed is + * secondary. + */ + +/** + * @defgroup DBusMemPoolInternals Memory pool implementation details + * @ingroup DBusInternals + * @brief DBusMemPool implementation details + * + * The guts of DBusMemPool. + * + * @{ + */ + +/** + * typedef so DBusFreedElement struct can refer to itself. + */ +typedef struct DBusFreedElement DBusFreedElement; + +/** + * struct representing an element on the free list. + * We just cast freed elements to this so we can + * make a list out of them. + */ +struct DBusFreedElement +{ + DBusFreedElement *next; /**< next element of the free list */ +}; + +/** + * The dummy size of the variable-length "elements" + * field in DBusMemBlock + */ +#define ELEMENT_PADDING 4 + +/** + * Typedef for DBusMemBlock so the struct can recursively + * point to itself. + */ +typedef struct DBusMemBlock DBusMemBlock; + +/** + * DBusMemBlock object represents a single malloc()-returned + * block that gets chunked up into objects in the memory pool. + */ +struct DBusMemBlock +{ + DBusMemBlock *next; /**< next block in the list, which is already used up; + * only saved so we can free all the blocks + * when we free the mem pool. + */ + + /* this is a long so that "elements" is aligned */ + long used_so_far; /**< bytes of this block already allocated as elements. */ + + unsigned char elements[ELEMENT_PADDING]; /**< the block data, actually allocated to required size */ +}; + +/** + * Internals fields of DBusMemPool + */ +struct DBusMemPool +{ + int element_size; /**< size of a single object in the pool */ + int block_size; /**< size of most recently allocated block */ + unsigned int zero_elements : 1; /**< whether to zero-init allocated elements */ + + DBusFreedElement *free_elements; /**< a free list of elements to recycle */ + DBusMemBlock *blocks; /**< blocks of memory from malloc() */ + int allocated_elements; /**< Count of outstanding allocated elements */ +}; + +/** @} */ + +/** + * @addtogroup DBusMemPool + * + * @{ + */ + +/** + * @typedef DBusMemPool + * + * Opaque object representing a memory pool. Memory pools allow + * avoiding per-malloc-block memory overhead when allocating a lot of + * small objects that are all the same size. They are slightly + * faster than calling malloc() also. + */ + +/** + * Creates a new memory pool, or returns #NULL on failure. Objects in + * the pool must be at least sizeof(void*) bytes each, due to the way + * memory pools work. To avoid creating 64 bit problems, this means at + * least 8 bytes on all platforms, unless you are 4 bytes on 32-bit + * and 8 bytes on 64-bit. + * + * @param element_size size of an element allocated from the pool. + * @param zero_elements whether to zero-initialize elements + * @returns the new pool or #NULL + */ +DBusMemPool* +_dbus_mem_pool_new (int element_size, + dbus_bool_t zero_elements) +{ + DBusMemPool *pool; + + pool = dbus_new0 (DBusMemPool, 1); + if (pool == NULL) + return NULL; + + /* Make the element size at least 8 bytes. */ + if (element_size < 8) + element_size = 8; + + /* these assertions are equivalent but the first is more clear + * to programmers that see it fail. + */ + _dbus_assert (element_size >= (int) sizeof (void*)); + _dbus_assert (element_size >= (int) sizeof (DBusFreedElement)); + + /* align the element size to a pointer boundary so we won't get bus + * errors under other architectures. + */ + pool->element_size = _DBUS_ALIGN_VALUE (element_size, sizeof (void *)); + + pool->zero_elements = zero_elements != FALSE; + + pool->allocated_elements = 0; + + /* pick a size for the first block; it increases + * for each block we need to allocate. This is + * actually half the initial block size + * since _dbus_mem_pool_alloc() unconditionally + * doubles it prior to creating a new block. */ + pool->block_size = pool->element_size * 8; + + _dbus_assert ((pool->block_size % + pool->element_size) == 0); + + return pool; +} + +/** + * Frees a memory pool (and all elements allocated from it). + * + * @param pool the memory pool. + */ +void +_dbus_mem_pool_free (DBusMemPool *pool) +{ + DBusMemBlock *block; + + block = pool->blocks; + while (block != NULL) + { + DBusMemBlock *next = block->next; + + dbus_free (block); + + block = next; + } + + dbus_free (pool); +} + +/** + * Allocates an object from the memory pool. + * The object must be freed with _dbus_mem_pool_dealloc(). + * + * @param pool the memory pool + * @returns the allocated object or #NULL if no memory. + */ +void* +_dbus_mem_pool_alloc (DBusMemPool *pool) +{ +#ifdef DBUS_BUILD_TESTS + if (_dbus_disable_mem_pools ()) + { + DBusMemBlock *block; + int alloc_size; + + /* This is obviously really silly, but it's + * debug-mode-only code that is compiled out + * when tests are disabled (_dbus_disable_mem_pools() + * is a constant expression FALSE so this block + * should vanish) + */ + + alloc_size = sizeof (DBusMemBlock) - ELEMENT_PADDING + + pool->element_size; + + if (pool->zero_elements) + block = dbus_malloc0 (alloc_size); + else + block = dbus_malloc (alloc_size); + + if (block != NULL) + { + block->next = pool->blocks; + pool->blocks = block; + pool->allocated_elements += 1; + + return (void*) &block->elements[0]; + } + else + return NULL; + } + else +#endif + { + if (_dbus_decrement_fail_alloc_counter ()) + { + _dbus_verbose (" FAILING mempool alloc\n"); + return NULL; + } + else if (pool->free_elements) + { + DBusFreedElement *element = pool->free_elements; + + pool->free_elements = pool->free_elements->next; + + if (pool->zero_elements) + memset (element, '\0', pool->element_size); + + pool->allocated_elements += 1; + + return element; + } + else + { + void *element; + + if (pool->blocks == NULL || + pool->blocks->used_so_far == pool->block_size) + { + /* Need a new block */ + DBusMemBlock *block; + int alloc_size; +#ifdef DBUS_BUILD_TESTS + int saved_counter; +#endif + + if (pool->block_size <= _DBUS_INT_MAX / 4) /* avoid overflow */ + { + /* use a larger block size for our next block */ + pool->block_size *= 2; + _dbus_assert ((pool->block_size % + pool->element_size) == 0); + } + + alloc_size = sizeof (DBusMemBlock) - ELEMENT_PADDING + pool->block_size; + +#ifdef DBUS_BUILD_TESTS + /* We save/restore the counter, so that memory pools won't + * cause a given function to have different number of + * allocations on different invocations. i.e. when testing + * we want consistent alloc patterns. So we skip our + * malloc here for purposes of failed alloc simulation. + */ + saved_counter = _dbus_get_fail_alloc_counter (); + _dbus_set_fail_alloc_counter (_DBUS_INT_MAX); +#endif + + if (pool->zero_elements) + block = dbus_malloc0 (alloc_size); + else + block = dbus_malloc (alloc_size); + +#ifdef DBUS_BUILD_TESTS + _dbus_set_fail_alloc_counter (saved_counter); + _dbus_assert (saved_counter == _dbus_get_fail_alloc_counter ()); +#endif + + if (block == NULL) + return NULL; + + block->used_so_far = 0; + block->next = pool->blocks; + pool->blocks = block; + } + + element = &pool->blocks->elements[pool->blocks->used_so_far]; + + pool->blocks->used_so_far += pool->element_size; + + pool->allocated_elements += 1; + + return element; + } + } +} + +/** + * Deallocates an object previously created with + * _dbus_mem_pool_alloc(). The previous object + * must have come from this same pool. + * @param pool the memory pool + * @param element the element earlier allocated. + * @returns #TRUE if there are no remaining allocated elements + */ +dbus_bool_t +_dbus_mem_pool_dealloc (DBusMemPool *pool, + void *element) +{ +#ifdef DBUS_BUILD_TESTS + if (_dbus_disable_mem_pools ()) + { + DBusMemBlock *block; + DBusMemBlock *prev; + + /* mmm, fast. ;-) debug-only code, so doesn't matter. */ + + prev = NULL; + block = pool->blocks; + + while (block != NULL) + { + if (block->elements == (unsigned char*) element) + { + if (prev) + prev->next = block->next; + else + pool->blocks = block->next; + + dbus_free (block); + + _dbus_assert (pool->allocated_elements > 0); + pool->allocated_elements -= 1; + + if (pool->allocated_elements == 0) + _dbus_assert (pool->blocks == NULL); + + return pool->blocks == NULL; + } + prev = block; + block = block->next; + } + + _dbus_assert_not_reached ("freed nonexistent block"); + return FALSE; + } + else +#endif + { + DBusFreedElement *freed; + + freed = element; + freed->next = pool->free_elements; + pool->free_elements = freed; + + _dbus_assert (pool->allocated_elements > 0); + pool->allocated_elements -= 1; + + return pool->allocated_elements == 0; + } +} + +/** @} */ + +#ifdef DBUS_BUILD_TESTS +#include "dbus-test.h" +#include +#include + +static void +time_for_size (int size) +{ + int i; + int j; + clock_t start; + clock_t end; +#define FREE_ARRAY_SIZE 512 +#define N_ITERATIONS FREE_ARRAY_SIZE * 512 + void *to_free[FREE_ARRAY_SIZE]; + DBusMemPool *pool; + + _dbus_verbose ("Timings for size %d\n", size); + + _dbus_verbose (" malloc\n"); + + start = clock (); + + i = 0; + j = 0; + while (i < N_ITERATIONS) + { + to_free[j] = dbus_malloc (size); + _dbus_assert (to_free[j] != NULL); /* in a real app of course this is wrong */ + + ++j; + + if (j == FREE_ARRAY_SIZE) + { + j = 0; + while (j < FREE_ARRAY_SIZE) + { + dbus_free (to_free[j]); + ++j; + } + + j = 0; + } + + ++i; + } + + end = clock (); + + _dbus_verbose (" created/destroyed %d elements in %g seconds\n", + N_ITERATIONS, (end - start) / (double) CLOCKS_PER_SEC); + + + + _dbus_verbose (" mempools\n"); + + start = clock (); + + pool = _dbus_mem_pool_new (size, FALSE); + + i = 0; + j = 0; + while (i < N_ITERATIONS) + { + to_free[j] = _dbus_mem_pool_alloc (pool); + _dbus_assert (to_free[j] != NULL); /* in a real app of course this is wrong */ + + ++j; + + if (j == FREE_ARRAY_SIZE) + { + j = 0; + while (j < FREE_ARRAY_SIZE) + { + _dbus_mem_pool_dealloc (pool, to_free[j]); + ++j; + } + + j = 0; + } + + ++i; + } + + _dbus_mem_pool_free (pool); + + end = clock (); + + _dbus_verbose (" created/destroyed %d elements in %g seconds\n", + N_ITERATIONS, (end - start) / (double) CLOCKS_PER_SEC); + + _dbus_verbose (" zeroed malloc\n"); + + start = clock (); + + i = 0; + j = 0; + while (i < N_ITERATIONS) + { + to_free[j] = dbus_malloc0 (size); + _dbus_assert (to_free[j] != NULL); /* in a real app of course this is wrong */ + + ++j; + + if (j == FREE_ARRAY_SIZE) + { + j = 0; + while (j < FREE_ARRAY_SIZE) + { + dbus_free (to_free[j]); + ++j; + } + + j = 0; + } + + ++i; + } + + end = clock (); + + _dbus_verbose (" created/destroyed %d elements in %g seconds\n", + N_ITERATIONS, (end - start) / (double) CLOCKS_PER_SEC); + + _dbus_verbose (" zeroed mempools\n"); + + start = clock (); + + pool = _dbus_mem_pool_new (size, TRUE); + + i = 0; + j = 0; + while (i < N_ITERATIONS) + { + to_free[j] = _dbus_mem_pool_alloc (pool); + _dbus_assert (to_free[j] != NULL); /* in a real app of course this is wrong */ + + ++j; + + if (j == FREE_ARRAY_SIZE) + { + j = 0; + while (j < FREE_ARRAY_SIZE) + { + _dbus_mem_pool_dealloc (pool, to_free[j]); + ++j; + } + + j = 0; + } + + ++i; + } + + _dbus_mem_pool_free (pool); + + end = clock (); + + _dbus_verbose (" created/destroyed %d elements in %g seconds\n", + N_ITERATIONS, (end - start) / (double) CLOCKS_PER_SEC); +} + +/** + * @ingroup DBusMemPoolInternals + * Unit test for DBusMemPool + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_mem_pool_test (void) +{ + int i; + int element_sizes[] = { 4, 8, 16, 50, 124 }; + + i = 0; + while (i < _DBUS_N_ELEMENTS (element_sizes)) + { + time_for_size (element_sizes[i]); + ++i; + } + + return TRUE; +} + +#endif /* DBUS_BUILD_TESTS */ diff --git a/src/dbus/dbus-mempool.h b/src/dbus/dbus-mempool.h new file mode 100644 index 0000000..459b45e --- /dev/null +++ b/src/dbus/dbus-mempool.h @@ -0,0 +1,44 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-mempool.h Memory pools + * + * Copyright (C) 2002 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef DBUS_MEMPOOL_H +#define DBUS_MEMPOOL_H + +#include +#include +#include + +DBUS_BEGIN_DECLS + +typedef struct DBusMemPool DBusMemPool; + +DBusMemPool* _dbus_mem_pool_new (int element_size, + dbus_bool_t zero_elements); +void _dbus_mem_pool_free (DBusMemPool *pool); +void* _dbus_mem_pool_alloc (DBusMemPool *pool); +dbus_bool_t _dbus_mem_pool_dealloc (DBusMemPool *pool, + void *element); + +DBUS_END_DECLS + +#endif /* DBUS_MEMPOOL_H */ diff --git a/src/dbus/dbus-message-factory.c b/src/dbus/dbus-message-factory.c new file mode 100644 index 0000000..8550ee8 --- /dev/null +++ b/src/dbus/dbus-message-factory.c @@ -0,0 +1,1228 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-message-factory.c Generator of valid and invalid message data for test suite + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +#ifdef DBUS_BUILD_TESTS +#include "dbus-message-factory.h" +#include "dbus-message-private.h" +#include "dbus-test.h" +#include + +typedef enum + { + CHANGE_TYPE_ADJUST, + CHANGE_TYPE_ABSOLUTE + } ChangeType; + +#define BYTE_ORDER_OFFSET 0 +#define TYPE_OFFSET 1 +#define BODY_LENGTH_OFFSET 4 +#define FIELDS_ARRAY_LENGTH_OFFSET 12 + +static void +iter_recurse (DBusMessageDataIter *iter) +{ + iter->depth += 1; + _dbus_assert (iter->depth < _DBUS_MESSAGE_DATA_MAX_NESTING); + _dbus_assert (iter->sequence_nos[iter->depth] >= 0); +} + +static int +iter_get_sequence (DBusMessageDataIter *iter) +{ + _dbus_assert (iter->sequence_nos[iter->depth] >= 0); + return iter->sequence_nos[iter->depth]; +} + +static void +iter_set_sequence (DBusMessageDataIter *iter, + int sequence) +{ + _dbus_assert (sequence >= 0); + iter->sequence_nos[iter->depth] = sequence; +} + +static void +iter_unrecurse (DBusMessageDataIter *iter) +{ + iter->depth -= 1; + _dbus_assert (iter->depth >= 0); +} + +static void +iter_next (DBusMessageDataIter *iter) +{ + iter->sequence_nos[iter->depth] += 1; +} + +static dbus_bool_t +iter_first_in_series (DBusMessageDataIter *iter) +{ + int i; + + i = iter->depth; + while (i < _DBUS_MESSAGE_DATA_MAX_NESTING) + { + if (iter->sequence_nos[i] != 0) + return FALSE; + ++i; + } + return TRUE; +} + +typedef dbus_bool_t (* DBusInnerGeneratorFunc) (DBusMessageDataIter *iter, + DBusMessage **message_p); +typedef dbus_bool_t (* DBusMessageGeneratorFunc) (DBusMessageDataIter *iter, + DBusString *data, + DBusValidity *expected_validity); + +static void +set_reply_serial (DBusMessage *message) +{ + if (message == NULL) + _dbus_assert_not_reached ("oom"); + if (!dbus_message_set_reply_serial (message, 100)) + _dbus_assert_not_reached ("oom"); +} + +static dbus_bool_t +generate_trivial_inner (DBusMessageDataIter *iter, + DBusMessage **message_p) +{ + DBusMessage *message; + + switch (iter_get_sequence (iter)) + { + case 0: + message = dbus_message_new_method_call ("org.freedesktop.TextEditor", + "/foo/bar", + "org.freedesktop.DocumentFactory", + "Create"); + break; + case 1: + message = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_RETURN); + set_reply_serial (message); + break; + case 2: + message = dbus_message_new_signal ("/foo/bar", + "org.freedesktop.DocumentFactory", + "Created"); + break; + case 3: + message = dbus_message_new (DBUS_MESSAGE_TYPE_ERROR); + + if (!dbus_message_set_error_name (message, + "org.freedesktop.TestErrorName")) + _dbus_assert_not_reached ("oom"); + + { + DBusMessageIter iter; + const char *v_STRING = "This is an error"; + + dbus_message_iter_init_append (message, &iter); + if (!dbus_message_iter_append_basic (&iter, + DBUS_TYPE_STRING, + &v_STRING)) + _dbus_assert_not_reached ("oom"); + } + + set_reply_serial (message); + break; + default: + return FALSE; + } + + if (message == NULL) + _dbus_assert_not_reached ("oom"); + + *message_p = message; + + return TRUE; +} + +static dbus_bool_t +generate_many_bodies_inner (DBusMessageDataIter *iter, + DBusMessage **message_p) +{ + DBusMessage *message; + DBusString signature; + DBusString body; + + /* Keeping this small makes things go faster */ + message = dbus_message_new_method_call ("o.z.F", + "/", + "o.z.B", + "Nah"); + if (message == NULL) + _dbus_assert_not_reached ("oom"); + + set_reply_serial (message); + + if (!_dbus_string_init (&signature) || !_dbus_string_init (&body)) + _dbus_assert_not_reached ("oom"); + + if (dbus_internal_do_not_use_generate_bodies (iter_get_sequence (iter), + message->byte_order, + &signature, &body)) + { + const char *v_SIGNATURE; + + v_SIGNATURE = _dbus_string_get_const_data (&signature); + if (!_dbus_header_set_field_basic (&message->header, + DBUS_HEADER_FIELD_SIGNATURE, + DBUS_TYPE_SIGNATURE, + &v_SIGNATURE)) + _dbus_assert_not_reached ("oom"); + + if (!_dbus_string_move (&body, 0, &message->body, 0)) + _dbus_assert_not_reached ("oom"); + + _dbus_marshal_set_uint32 (&message->header.data, BODY_LENGTH_OFFSET, + _dbus_string_get_length (&message->body), + message->byte_order); + + *message_p = message; + } + else + { + dbus_message_unref (message); + *message_p = NULL; + } + + _dbus_string_free (&signature); + _dbus_string_free (&body); + + return *message_p != NULL; +} + +static void +generate_from_message (DBusString *data, + DBusValidity *expected_validity, + DBusMessage *message) +{ + dbus_message_set_serial (message, 1); + dbus_message_lock (message); + + *expected_validity = DBUS_VALID; + + /* move for efficiency, since we'll nuke the message anyway */ + if (!_dbus_string_move (&message->header.data, 0, + data, 0)) + _dbus_assert_not_reached ("oom"); + + if (!_dbus_string_copy (&message->body, 0, + data, _dbus_string_get_length (data))) + _dbus_assert_not_reached ("oom"); +} + +static dbus_bool_t +generate_outer (DBusMessageDataIter *iter, + DBusString *data, + DBusValidity *expected_validity, + DBusInnerGeneratorFunc func) +{ + DBusMessage *message; + + message = NULL; + if (!(*func)(iter, &message)) + return FALSE; + + iter_next (iter); + + _dbus_assert (message != NULL); + + generate_from_message (data, expected_validity, message); + + dbus_message_unref (message); + + return TRUE; +} + +static dbus_bool_t +generate_trivial (DBusMessageDataIter *iter, + DBusString *data, + DBusValidity *expected_validity) +{ + return generate_outer (iter, data, expected_validity, + generate_trivial_inner); +} + +static dbus_bool_t +generate_many_bodies (DBusMessageDataIter *iter, + DBusString *data, + DBusValidity *expected_validity) +{ + return generate_outer (iter, data, expected_validity, + generate_many_bodies_inner); +} + +static DBusMessage* +simple_method_call (void) +{ + DBusMessage *message; + /* Keeping this small makes stuff go faster */ + message = dbus_message_new_method_call ("o.b.Q", + "/f/b", + "o.b.Z", + "Fro"); + if (message == NULL) + _dbus_assert_not_reached ("oom"); + return message; +} + +static DBusMessage* +simple_signal (void) +{ + DBusMessage *message; + message = dbus_message_new_signal ("/f/b", + "o.b.Z", + "Fro"); + if (message == NULL) + _dbus_assert_not_reached ("oom"); + return message; +} + +static DBusMessage* +simple_method_return (void) +{ + DBusMessage *message; + message = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_RETURN); + if (message == NULL) + _dbus_assert_not_reached ("oom"); + + set_reply_serial (message); + + return message; +} + +static DBusMessage* +simple_error (void) +{ + DBusMessage *message; + message = dbus_message_new (DBUS_MESSAGE_TYPE_ERROR); + if (message == NULL) + _dbus_assert_not_reached ("oom"); + + if (!dbus_message_set_error_name (message, "foo.bar")) + _dbus_assert_not_reached ("oom"); + + set_reply_serial (message); + + return message; +} + +static dbus_bool_t +generate_special (DBusMessageDataIter *iter, + DBusString *data, + DBusValidity *expected_validity) +{ + int item_seq; + DBusMessage *message; + int pos; + dbus_int32_t v_INT32; + + _dbus_assert (_dbus_string_get_length (data) == 0); + + message = NULL; + pos = -1; + v_INT32 = 42; + item_seq = iter_get_sequence (iter); + + if (item_seq == 0) + { + message = simple_method_call (); + if (!dbus_message_append_args (message, + DBUS_TYPE_INT32, &v_INT32, + DBUS_TYPE_INT32, &v_INT32, + DBUS_TYPE_INT32, &v_INT32, + DBUS_TYPE_INVALID)) + _dbus_assert_not_reached ("oom"); + + _dbus_header_get_field_raw (&message->header, + DBUS_HEADER_FIELD_SIGNATURE, + NULL, &pos); + generate_from_message (data, expected_validity, message); + + /* set an invalid typecode */ + _dbus_string_set_byte (data, pos + 1, '$'); + + *expected_validity = DBUS_INVALID_UNKNOWN_TYPECODE; + } + else if (item_seq == 1) + { + char long_sig[DBUS_MAXIMUM_TYPE_RECURSION_DEPTH+2]; + const char *v_STRING; + int i; + + message = simple_method_call (); + if (!dbus_message_append_args (message, + DBUS_TYPE_INT32, &v_INT32, + DBUS_TYPE_INT32, &v_INT32, + DBUS_TYPE_INT32, &v_INT32, + DBUS_TYPE_INVALID)) + _dbus_assert_not_reached ("oom"); + + i = 0; + while (i < (DBUS_MAXIMUM_TYPE_RECURSION_DEPTH + 1)) + { + long_sig[i] = DBUS_TYPE_ARRAY; + ++i; + } + long_sig[i] = DBUS_TYPE_INVALID; + + v_STRING = long_sig; + if (!_dbus_header_set_field_basic (&message->header, + DBUS_HEADER_FIELD_SIGNATURE, + DBUS_TYPE_SIGNATURE, + &v_STRING)) + _dbus_assert_not_reached ("oom"); + + _dbus_header_get_field_raw (&message->header, + DBUS_HEADER_FIELD_SIGNATURE, + NULL, &pos); + generate_from_message (data, expected_validity, message); + + *expected_validity = DBUS_INVALID_EXCEEDED_MAXIMUM_ARRAY_RECURSION; + } + else if (item_seq == 2) + { + char long_sig[DBUS_MAXIMUM_TYPE_RECURSION_DEPTH*2+4]; + const char *v_STRING; + int i; + + message = simple_method_call (); + if (!dbus_message_append_args (message, + DBUS_TYPE_INT32, &v_INT32, + DBUS_TYPE_INT32, &v_INT32, + DBUS_TYPE_INT32, &v_INT32, + DBUS_TYPE_INVALID)) + _dbus_assert_not_reached ("oom"); + + i = 0; + while (i <= (DBUS_MAXIMUM_TYPE_RECURSION_DEPTH + 1)) + { + long_sig[i] = DBUS_STRUCT_BEGIN_CHAR; + ++i; + } + + long_sig[i] = DBUS_TYPE_INT32; + ++i; + + while (i < (DBUS_MAXIMUM_TYPE_RECURSION_DEPTH*2 + 3)) + { + long_sig[i] = DBUS_STRUCT_END_CHAR; + ++i; + } + long_sig[i] = DBUS_TYPE_INVALID; + + v_STRING = long_sig; + if (!_dbus_header_set_field_basic (&message->header, + DBUS_HEADER_FIELD_SIGNATURE, + DBUS_TYPE_SIGNATURE, + &v_STRING)) + _dbus_assert_not_reached ("oom"); + + _dbus_header_get_field_raw (&message->header, + DBUS_HEADER_FIELD_SIGNATURE, + NULL, &pos); + generate_from_message (data, expected_validity, message); + + *expected_validity = DBUS_INVALID_EXCEEDED_MAXIMUM_STRUCT_RECURSION; + } + else if (item_seq == 3) + { + message = simple_method_call (); + if (!dbus_message_append_args (message, + DBUS_TYPE_INT32, &v_INT32, + DBUS_TYPE_INT32, &v_INT32, + DBUS_TYPE_INT32, &v_INT32, + DBUS_TYPE_INVALID)) + _dbus_assert_not_reached ("oom"); + + _dbus_header_get_field_raw (&message->header, + DBUS_HEADER_FIELD_SIGNATURE, + NULL, &pos); + generate_from_message (data, expected_validity, message); + + _dbus_string_set_byte (data, pos + 1, DBUS_STRUCT_BEGIN_CHAR); + + *expected_validity = DBUS_INVALID_STRUCT_STARTED_BUT_NOT_ENDED; + } + else if (item_seq == 4) + { + message = simple_method_call (); + if (!dbus_message_append_args (message, + DBUS_TYPE_INT32, &v_INT32, + DBUS_TYPE_INT32, &v_INT32, + DBUS_TYPE_INT32, &v_INT32, + DBUS_TYPE_INVALID)) + _dbus_assert_not_reached ("oom"); + + _dbus_header_get_field_raw (&message->header, + DBUS_HEADER_FIELD_SIGNATURE, + NULL, &pos); + generate_from_message (data, expected_validity, message); + + _dbus_string_set_byte (data, pos + 1, DBUS_STRUCT_END_CHAR); + + *expected_validity = DBUS_INVALID_STRUCT_ENDED_BUT_NOT_STARTED; + } + else if (item_seq == 5) + { + message = simple_method_call (); + if (!dbus_message_append_args (message, + DBUS_TYPE_INT32, &v_INT32, + DBUS_TYPE_INT32, &v_INT32, + DBUS_TYPE_INT32, &v_INT32, + DBUS_TYPE_INVALID)) + _dbus_assert_not_reached ("oom"); + + _dbus_header_get_field_raw (&message->header, + DBUS_HEADER_FIELD_SIGNATURE, + NULL, &pos); + generate_from_message (data, expected_validity, message); + + _dbus_string_set_byte (data, pos + 1, DBUS_STRUCT_BEGIN_CHAR); + _dbus_string_set_byte (data, pos + 2, DBUS_STRUCT_END_CHAR); + + *expected_validity = DBUS_INVALID_STRUCT_HAS_NO_FIELDS; + } + else if (item_seq == 6) + { + message = simple_method_call (); + generate_from_message (data, expected_validity, message); + + _dbus_string_set_byte (data, TYPE_OFFSET, DBUS_MESSAGE_TYPE_INVALID); + + *expected_validity = DBUS_INVALID_BAD_MESSAGE_TYPE; + } + else if (item_seq == 7) + { + /* Messages of unknown type are considered valid */ + message = simple_method_call (); + generate_from_message (data, expected_validity, message); + + _dbus_string_set_byte (data, TYPE_OFFSET, 100); + + *expected_validity = DBUS_VALID; + } + else if (item_seq == 8) + { + message = simple_method_call (); + generate_from_message (data, expected_validity, message); + + _dbus_marshal_set_uint32 (data, BODY_LENGTH_OFFSET, + DBUS_MAXIMUM_MESSAGE_LENGTH / 2 + 4, + message->byte_order); + _dbus_marshal_set_uint32 (data, FIELDS_ARRAY_LENGTH_OFFSET, + DBUS_MAXIMUM_MESSAGE_LENGTH / 2 + 4, + message->byte_order); + *expected_validity = DBUS_INVALID_MESSAGE_TOO_LONG; + } + else if (item_seq == 9) + { + const char *v_STRING = "not a valid bus name"; + message = simple_method_call (); + + if (!_dbus_header_set_field_basic (&message->header, + DBUS_HEADER_FIELD_SENDER, + DBUS_TYPE_STRING, &v_STRING)) + _dbus_assert_not_reached ("oom"); + + generate_from_message (data, expected_validity, message); + + *expected_validity = DBUS_INVALID_BAD_SENDER; + } + else if (item_seq == 10) + { + message = simple_method_call (); + + if (!dbus_message_set_interface (message, DBUS_INTERFACE_LOCAL)) + _dbus_assert_not_reached ("oom"); + + generate_from_message (data, expected_validity, message); + + *expected_validity = DBUS_INVALID_USES_LOCAL_INTERFACE; + } + else if (item_seq == 11) + { + message = simple_method_call (); + + if (!dbus_message_set_path (message, DBUS_PATH_LOCAL)) + _dbus_assert_not_reached ("oom"); + + generate_from_message (data, expected_validity, message); + + *expected_validity = DBUS_INVALID_USES_LOCAL_PATH; + } + else if (item_seq == 12) + { + /* Method calls don't have to have interface */ + message = simple_method_call (); + + if (!dbus_message_set_interface (message, NULL)) + _dbus_assert_not_reached ("oom"); + + generate_from_message (data, expected_validity, message); + + *expected_validity = DBUS_VALID; + } + else if (item_seq == 13) + { + /* Signals require an interface */ + message = simple_signal (); + + if (!dbus_message_set_interface (message, NULL)) + _dbus_assert_not_reached ("oom"); + + generate_from_message (data, expected_validity, message); + + *expected_validity = DBUS_INVALID_MISSING_INTERFACE; + } + else if (item_seq == 14) + { + message = simple_method_return (); + + if (!_dbus_header_delete_field (&message->header, DBUS_HEADER_FIELD_REPLY_SERIAL)) + _dbus_assert_not_reached ("oom"); + + generate_from_message (data, expected_validity, message); + + *expected_validity = DBUS_INVALID_MISSING_REPLY_SERIAL; + } + else if (item_seq == 15) + { + message = simple_error (); + + if (!dbus_message_set_error_name (message, NULL)) + _dbus_assert_not_reached ("oom"); + + generate_from_message (data, expected_validity, message); + + *expected_validity = DBUS_INVALID_MISSING_ERROR_NAME; + } + else if (item_seq == 16) + { + char long_sig[DBUS_MAXIMUM_TYPE_RECURSION_DEPTH*4+10]; + const char *v_STRING; + int i; + int n_begins; + + message = simple_method_call (); + if (!dbus_message_append_args (message, + DBUS_TYPE_INT32, &v_INT32, + DBUS_TYPE_INT32, &v_INT32, + DBUS_TYPE_INT32, &v_INT32, + DBUS_TYPE_INVALID)) + _dbus_assert_not_reached ("oom"); + + i = 0; + while (i <= (DBUS_MAXIMUM_TYPE_RECURSION_DEPTH*3 + 3)) + { + long_sig[i] = DBUS_TYPE_ARRAY; + ++i; + long_sig[i] = DBUS_DICT_ENTRY_BEGIN_CHAR; + ++i; + long_sig[i] = DBUS_TYPE_INT32; + ++i; + } + n_begins = i / 3; + + long_sig[i] = DBUS_TYPE_INT32; + ++i; + + while (n_begins > 0) + { + long_sig[i] = DBUS_DICT_ENTRY_END_CHAR; + ++i; + n_begins -= 1; + } + long_sig[i] = DBUS_TYPE_INVALID; + + v_STRING = long_sig; + if (!_dbus_header_set_field_basic (&message->header, + DBUS_HEADER_FIELD_SIGNATURE, + DBUS_TYPE_SIGNATURE, + &v_STRING)) + _dbus_assert_not_reached ("oom"); + + _dbus_header_get_field_raw (&message->header, + DBUS_HEADER_FIELD_SIGNATURE, + NULL, &pos); + generate_from_message (data, expected_validity, message); + + *expected_validity = DBUS_INVALID_EXCEEDED_MAXIMUM_DICT_ENTRY_RECURSION; + } + else if (item_seq == 17) + { + message = simple_method_call (); + if (!dbus_message_append_args (message, + DBUS_TYPE_INT32, &v_INT32, + DBUS_TYPE_INT32, &v_INT32, + DBUS_TYPE_INT32, &v_INT32, + DBUS_TYPE_INVALID)) + _dbus_assert_not_reached ("oom"); + + _dbus_header_get_field_raw (&message->header, + DBUS_HEADER_FIELD_SIGNATURE, + NULL, &pos); + generate_from_message (data, expected_validity, message); + + _dbus_string_set_byte (data, pos + 1, DBUS_TYPE_ARRAY); + _dbus_string_set_byte (data, pos + 2, DBUS_DICT_ENTRY_BEGIN_CHAR); + + *expected_validity = DBUS_INVALID_DICT_ENTRY_STARTED_BUT_NOT_ENDED; + } + else if (item_seq == 18) + { + message = simple_method_call (); + if (!dbus_message_append_args (message, + DBUS_TYPE_INT32, &v_INT32, + DBUS_TYPE_INT32, &v_INT32, + DBUS_TYPE_INT32, &v_INT32, + DBUS_TYPE_INVALID)) + _dbus_assert_not_reached ("oom"); + + _dbus_header_get_field_raw (&message->header, + DBUS_HEADER_FIELD_SIGNATURE, + NULL, &pos); + generate_from_message (data, expected_validity, message); + + _dbus_string_set_byte (data, pos + 1, DBUS_DICT_ENTRY_END_CHAR); + + *expected_validity = DBUS_INVALID_DICT_ENTRY_ENDED_BUT_NOT_STARTED; + } + else if (item_seq == 19) + { + message = simple_method_call (); + if (!dbus_message_append_args (message, + DBUS_TYPE_INT32, &v_INT32, + DBUS_TYPE_INT32, &v_INT32, + DBUS_TYPE_INT32, &v_INT32, + DBUS_TYPE_INVALID)) + _dbus_assert_not_reached ("oom"); + + _dbus_header_get_field_raw (&message->header, + DBUS_HEADER_FIELD_SIGNATURE, + NULL, &pos); + generate_from_message (data, expected_validity, message); + + _dbus_string_set_byte (data, pos + 1, DBUS_TYPE_ARRAY); + _dbus_string_set_byte (data, pos + 2, DBUS_DICT_ENTRY_BEGIN_CHAR); + _dbus_string_set_byte (data, pos + 3, DBUS_DICT_ENTRY_END_CHAR); + + *expected_validity = DBUS_INVALID_DICT_ENTRY_HAS_NO_FIELDS; + } + else + { + return FALSE; + } + + if (message) + dbus_message_unref (message); + + iter_next (iter); + return TRUE; +} + +static dbus_bool_t +generate_wrong_length (DBusMessageDataIter *iter, + DBusString *data, + DBusValidity *expected_validity) +{ + int lengths[] = { -42, -17, -16, -15, -9, -8, -7, -6, -5, -4, -3, -2, -1, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 15, 16, 30 }; + int adjust; + int len_seq; + + restart: + len_seq = iter_get_sequence (iter); + if (len_seq == _DBUS_N_ELEMENTS (lengths)) + return FALSE; + + _dbus_assert (len_seq < _DBUS_N_ELEMENTS (lengths)); + + iter_recurse (iter); + if (!generate_many_bodies (iter, data, expected_validity)) + { + iter_set_sequence (iter, 0); /* reset to first body */ + iter_unrecurse (iter); + iter_next (iter); /* next length adjustment */ + goto restart; + } + iter_unrecurse (iter); + + adjust = lengths[len_seq]; + + if (adjust < 0) + { + if ((_dbus_string_get_length (data) + adjust) < DBUS_MINIMUM_HEADER_SIZE) + _dbus_string_set_length (data, DBUS_MINIMUM_HEADER_SIZE); + else + _dbus_string_shorten (data, - adjust); + *expected_validity = DBUS_INVALID_FOR_UNKNOWN_REASON; + } + else + { + if (!_dbus_string_lengthen (data, adjust)) + _dbus_assert_not_reached ("oom"); + *expected_validity = DBUS_INVALID_TOO_MUCH_DATA; + } + + /* Fixup lengths */ + { + int old_body_len; + int new_body_len; + int byte_order; + + _dbus_assert (_dbus_string_get_length (data) >= DBUS_MINIMUM_HEADER_SIZE); + + byte_order = _dbus_string_get_byte (data, BYTE_ORDER_OFFSET); + old_body_len = _dbus_marshal_read_uint32 (data, + BODY_LENGTH_OFFSET, + byte_order, + NULL); + _dbus_assert (old_body_len < _dbus_string_get_length (data)); + new_body_len = old_body_len + adjust; + if (new_body_len < 0) + { + new_body_len = 0; + /* we just munged the header, and aren't sure how */ + *expected_validity = DBUS_VALIDITY_UNKNOWN; + } + + _dbus_verbose ("changing body len from %u to %u by adjust %d\n", + old_body_len, new_body_len, adjust); + + _dbus_marshal_set_uint32 (data, BODY_LENGTH_OFFSET, + new_body_len, + byte_order); + } + + return TRUE; +} + +static dbus_bool_t +generate_byte_changed (DBusMessageDataIter *iter, + DBusString *data, + DBusValidity *expected_validity) +{ + int byte_seq; + int v_BYTE; + + /* This is a little convoluted to make the bodies the + * outer loop and each byte of each body the inner + * loop + */ + + restart: + if (!generate_many_bodies (iter, data, expected_validity)) + return FALSE; + + iter_recurse (iter); + byte_seq = iter_get_sequence (iter); + iter_next (iter); + iter_unrecurse (iter); + + if (byte_seq == _dbus_string_get_length (data)) + { + _dbus_string_set_length (data, 0); + /* reset byte count */ + iter_recurse (iter); + iter_set_sequence (iter, 0); + iter_unrecurse (iter); + goto restart; + } + else + { + /* Undo the "next" in generate_many_bodies */ + iter_set_sequence (iter, iter_get_sequence (iter) - 1); + } + + _dbus_assert (byte_seq < _dbus_string_get_length (data)); + v_BYTE = _dbus_string_get_byte (data, byte_seq); + v_BYTE += byte_seq; /* arbitrary but deterministic change to the byte */ + _dbus_string_set_byte (data, byte_seq, v_BYTE); + *expected_validity = DBUS_VALIDITY_UNKNOWN; + + return TRUE; +} + +static dbus_bool_t +find_next_typecode (DBusMessageDataIter *iter, + DBusString *data, + DBusValidity *expected_validity) +{ + int body_seq; + int byte_seq; + int base_depth; + + base_depth = iter->depth; + + restart: + _dbus_assert (iter->depth == (base_depth + 0)); + _dbus_string_set_length (data, 0); + + body_seq = iter_get_sequence (iter); + + if (!generate_many_bodies (iter, data, expected_validity)) + return FALSE; + /* Undo the "next" in generate_many_bodies */ + iter_set_sequence (iter, body_seq); + + iter_recurse (iter); + while (TRUE) + { + _dbus_assert (iter->depth == (base_depth + 1)); + + byte_seq = iter_get_sequence (iter); + + _dbus_assert (byte_seq <= _dbus_string_get_length (data)); + + if (byte_seq == _dbus_string_get_length (data)) + { + /* reset byte count */ + iter_set_sequence (iter, 0); + iter_unrecurse (iter); + _dbus_assert (iter->depth == (base_depth + 0)); + iter_next (iter); /* go to the next body */ + goto restart; + } + + _dbus_assert (byte_seq < _dbus_string_get_length (data)); + + if (_dbus_type_is_valid (_dbus_string_get_byte (data, byte_seq))) + break; + else + iter_next (iter); + } + + _dbus_assert (byte_seq == iter_get_sequence (iter)); + _dbus_assert (byte_seq < _dbus_string_get_length (data)); + + iter_unrecurse (iter); + + _dbus_assert (iter->depth == (base_depth + 0)); + + return TRUE; +} + +static const int typecodes[] = { + DBUS_TYPE_INVALID, + DBUS_TYPE_BYTE, + DBUS_TYPE_BOOLEAN, + DBUS_TYPE_INT16, + DBUS_TYPE_UINT16, + DBUS_TYPE_INT32, + DBUS_TYPE_UINT32, + DBUS_TYPE_INT64, + DBUS_TYPE_UINT64, + DBUS_TYPE_DOUBLE, + DBUS_TYPE_STRING, + DBUS_TYPE_OBJECT_PATH, + DBUS_TYPE_SIGNATURE, + DBUS_TYPE_ARRAY, + DBUS_TYPE_VARIANT, + DBUS_STRUCT_BEGIN_CHAR, + DBUS_STRUCT_END_CHAR, + DBUS_DICT_ENTRY_BEGIN_CHAR, + DBUS_DICT_ENTRY_END_CHAR, + 255 /* random invalid typecode */ +}; + +static dbus_bool_t +generate_typecode_changed (DBusMessageDataIter *iter, + DBusString *data, + DBusValidity *expected_validity) +{ + int byte_seq; + int typecode_seq; + int base_depth; + + base_depth = iter->depth; + + restart: + _dbus_assert (iter->depth == (base_depth + 0)); + _dbus_string_set_length (data, 0); + + if (!find_next_typecode (iter, data, expected_validity)) + return FALSE; + + iter_recurse (iter); + byte_seq = iter_get_sequence (iter); + + _dbus_assert (byte_seq < _dbus_string_get_length (data)); + + iter_recurse (iter); + typecode_seq = iter_get_sequence (iter); + iter_next (iter); + + _dbus_assert (typecode_seq <= _DBUS_N_ELEMENTS (typecodes)); + + if (typecode_seq == _DBUS_N_ELEMENTS (typecodes)) + { + _dbus_assert (iter->depth == (base_depth + 2)); + iter_set_sequence (iter, 0); /* reset typecode sequence */ + iter_unrecurse (iter); + _dbus_assert (iter->depth == (base_depth + 1)); + iter_next (iter); /* go to the next byte_seq */ + iter_unrecurse (iter); + _dbus_assert (iter->depth == (base_depth + 0)); + goto restart; + } + + _dbus_assert (iter->depth == (base_depth + 2)); + iter_unrecurse (iter); + _dbus_assert (iter->depth == (base_depth + 1)); + iter_unrecurse (iter); + _dbus_assert (iter->depth == (base_depth + 0)); + +#if 0 + printf ("Changing byte %d in message %d to %c\n", + byte_seq, iter_get_sequence (iter), typecodes[typecode_seq]); +#endif + + _dbus_string_set_byte (data, byte_seq, typecodes[typecode_seq]); + *expected_validity = DBUS_VALIDITY_UNKNOWN; + return TRUE; +} + +typedef struct +{ + ChangeType type; + dbus_uint32_t value; /* cast to signed for adjusts */ +} UIntChange; + +static const UIntChange uint32_changes[] = { + { CHANGE_TYPE_ADJUST, (dbus_uint32_t) -1 }, + { CHANGE_TYPE_ADJUST, (dbus_uint32_t) -2 }, + { CHANGE_TYPE_ADJUST, (dbus_uint32_t) -3 }, + { CHANGE_TYPE_ADJUST, (dbus_uint32_t) 1 }, + { CHANGE_TYPE_ADJUST, (dbus_uint32_t) 2 }, + { CHANGE_TYPE_ADJUST, (dbus_uint32_t) 3 }, + { CHANGE_TYPE_ABSOLUTE, _DBUS_UINT32_MAX }, + { CHANGE_TYPE_ABSOLUTE, 0 }, + { CHANGE_TYPE_ABSOLUTE, 1 }, + { CHANGE_TYPE_ABSOLUTE, _DBUS_UINT32_MAX - 1 }, + { CHANGE_TYPE_ABSOLUTE, _DBUS_UINT32_MAX - 5 } +}; + +static dbus_bool_t +generate_uint32_changed (DBusMessageDataIter *iter, + DBusString *data, + DBusValidity *expected_validity) +{ + int body_seq; + int byte_seq; + int change_seq; + dbus_uint32_t v_UINT32; + int byte_order; + const UIntChange *change; + int base_depth; + + /* Outer loop is each body, next loop is each change, + * inner loop is each change location + */ + + base_depth = iter->depth; + + next_body: + _dbus_assert (iter->depth == (base_depth + 0)); + _dbus_string_set_length (data, 0); + body_seq = iter_get_sequence (iter); + + if (!generate_many_bodies (iter, data, expected_validity)) + return FALSE; + + _dbus_assert (iter->depth == (base_depth + 0)); + + iter_set_sequence (iter, body_seq); /* undo the "next" from generate_many_bodies */ + iter_recurse (iter); + next_change: + _dbus_assert (iter->depth == (base_depth + 1)); + change_seq = iter_get_sequence (iter); + + if (change_seq == _DBUS_N_ELEMENTS (uint32_changes)) + { + /* Reset change count */ + iter_set_sequence (iter, 0); + iter_unrecurse (iter); + iter_next (iter); + goto next_body; + } + + _dbus_assert (iter->depth == (base_depth + 1)); + + iter_recurse (iter); + _dbus_assert (iter->depth == (base_depth + 2)); + byte_seq = iter_get_sequence (iter); + /* skip 4 bytes at a time */ + iter_next (iter); + iter_next (iter); + iter_next (iter); + iter_next (iter); + iter_unrecurse (iter); + + _dbus_assert (_DBUS_ALIGN_VALUE (byte_seq, 4) == (unsigned) byte_seq); + if (byte_seq >= (_dbus_string_get_length (data) - 4)) + { + /* reset byte count */ + _dbus_assert (iter->depth == (base_depth + 1)); + iter_recurse (iter); + _dbus_assert (iter->depth == (base_depth + 2)); + iter_set_sequence (iter, 0); + iter_unrecurse (iter); + _dbus_assert (iter->depth == (base_depth + 1)); + iter_next (iter); + goto next_change; + } + + _dbus_assert (byte_seq <= (_dbus_string_get_length (data) - 4)); + + byte_order = _dbus_string_get_byte (data, BYTE_ORDER_OFFSET); + + v_UINT32 = _dbus_marshal_read_uint32 (data, byte_seq, byte_order, NULL); + + change = &uint32_changes[change_seq]; + + if (change->type == CHANGE_TYPE_ADJUST) + { + v_UINT32 += (int) change->value; + } + else + { + v_UINT32 = change->value; + } + +#if 0 + printf ("body %d change %d pos %d ", + body_seq, change_seq, byte_seq); + + if (change->type == CHANGE_TYPE_ADJUST) + printf ("adjust by %d", (int) change->value); + else + printf ("set to %u", change->value); + + printf (" \t%u -> %u\n", + _dbus_marshal_read_uint32 (data, byte_seq, byte_order, NULL), + v_UINT32); +#endif + + _dbus_marshal_set_uint32 (data, byte_seq, v_UINT32, byte_order); + *expected_validity = DBUS_VALIDITY_UNKNOWN; + + _dbus_assert (iter->depth == (base_depth + 1)); + iter_unrecurse (iter); + _dbus_assert (iter->depth == (base_depth + 0)); + + return TRUE; +} + +typedef struct +{ + const char *name; + DBusMessageGeneratorFunc func; +} DBusMessageGenerator; + +static const DBusMessageGenerator generators[] = { + { "trivial example of each message type", generate_trivial }, + { "assorted arguments", generate_many_bodies }, + { "assorted special cases", generate_special }, + { "each uint32 modified", generate_uint32_changed }, + { "wrong body lengths", generate_wrong_length }, + { "each byte modified", generate_byte_changed }, +#if 0 + /* This is really expensive and doesn't add too much coverage */ + { "change each typecode", generate_typecode_changed } +#endif +}; + +void +_dbus_message_data_free (DBusMessageData *data) +{ + _dbus_string_free (&data->data); +} + +void +_dbus_message_data_iter_init (DBusMessageDataIter *iter) +{ + int i; + + iter->depth = 0; + i = 0; + while (i < _DBUS_MESSAGE_DATA_MAX_NESTING) + { + iter->sequence_nos[i] = 0; + ++i; + } + iter->count = 0; +} + +dbus_bool_t +_dbus_message_data_iter_get_and_next (DBusMessageDataIter *iter, + DBusMessageData *data) +{ + DBusMessageGeneratorFunc func; + int generator; + + restart: + generator = iter_get_sequence (iter); + + if (generator == _DBUS_N_ELEMENTS (generators)) + return FALSE; + + iter_recurse (iter); + + if (iter_first_in_series (iter)) + { + printf (" testing message loading: %s ", generators[generator].name); + fflush (stdout); + } + + func = generators[generator].func; + + if (!_dbus_string_init (&data->data)) + _dbus_assert_not_reached ("oom"); + + if ((*func)(iter, &data->data, &data->expected_validity)) + ; + else + { + iter_set_sequence (iter, 0); + iter_unrecurse (iter); + iter_next (iter); /* next generator */ + _dbus_string_free (&data->data); + printf ("%d test loads cumulative\n", iter->count); + goto restart; + } + iter_unrecurse (iter); + + iter->count += 1; + return TRUE; +} + +#endif /* !DOXYGEN_SHOULD_SKIP_THIS */ + +#endif /* DBUS_BUILD_TESTS */ diff --git a/src/dbus/dbus-message-factory.h b/src/dbus/dbus-message-factory.h new file mode 100644 index 0000000..de5cc65 --- /dev/null +++ b/src/dbus/dbus-message-factory.h @@ -0,0 +1,61 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-message-factory.h Generator of valid and invalid message data for test suite + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef DBUS_MESSAGE_FACTORY_H +#define DBUS_MESSAGE_FACTORY_H + +#ifdef DBUS_BUILD_TESTS + +#include +#include +#include + +DBUS_BEGIN_DECLS + +typedef struct +{ + DBusValidity expected_validity; + + DBusString data; + +} DBusMessageData; + +#define _DBUS_MESSAGE_DATA_MAX_NESTING 10 +typedef struct +{ + int sequence_nos[_DBUS_MESSAGE_DATA_MAX_NESTING]; + int depth; + int count; +} DBusMessageDataIter; + +void _dbus_message_data_free (DBusMessageData *data); +void _dbus_message_data_iter_init (DBusMessageDataIter *iter); +dbus_bool_t _dbus_message_data_iter_get_and_next (DBusMessageDataIter *iter, + DBusMessageData *data); + + +DBUS_END_DECLS + +#endif /* DBUS_BUILD_TESTS */ + +#endif /* DBUS_MESSAGE_FACTORY_H */ diff --git a/src/dbus/dbus-message-internal.h b/src/dbus/dbus-message-internal.h new file mode 100644 index 0000000..2e995b4 --- /dev/null +++ b/src/dbus/dbus-message-internal.h @@ -0,0 +1,72 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-message-internal.h DBusMessage object internal interfaces + * + * Copyright (C) 2002 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef DBUS_MESSAGE_INTERNAL_H +#define DBUS_MESSAGE_INTERNAL_H + +#include +#include +#include + +DBUS_BEGIN_DECLS + +typedef struct DBusMessageLoader DBusMessageLoader; + +void _dbus_message_get_network_data (DBusMessage *message, + const DBusString **header, + const DBusString **body); + +void _dbus_message_lock (DBusMessage *message); +void _dbus_message_unlock (DBusMessage *message); +dbus_bool_t _dbus_message_add_size_counter (DBusMessage *message, + DBusCounter *counter); +void _dbus_message_add_size_counter_link (DBusMessage *message, + DBusList *link); +void _dbus_message_remove_size_counter (DBusMessage *message, + DBusCounter *counter, + DBusList **link_return); + +DBusMessageLoader* _dbus_message_loader_new (void); +DBusMessageLoader* _dbus_message_loader_ref (DBusMessageLoader *loader); +void _dbus_message_loader_unref (DBusMessageLoader *loader); + +void _dbus_message_loader_get_buffer (DBusMessageLoader *loader, + DBusString **buffer); +void _dbus_message_loader_return_buffer (DBusMessageLoader *loader, + DBusString *buffer, + int bytes_read); +dbus_bool_t _dbus_message_loader_queue_messages (DBusMessageLoader *loader); +DBusMessage* _dbus_message_loader_peek_message (DBusMessageLoader *loader); +DBusMessage* _dbus_message_loader_pop_message (DBusMessageLoader *loader); +DBusList* _dbus_message_loader_pop_message_link (DBusMessageLoader *loader); +void _dbus_message_loader_putback_message_link (DBusMessageLoader *loader, + DBusList *link); + +dbus_bool_t _dbus_message_loader_get_is_corrupted (DBusMessageLoader *loader); + +void _dbus_message_loader_set_max_message_size (DBusMessageLoader *loader, + long size); +long _dbus_message_loader_get_max_message_size (DBusMessageLoader *loader); + +DBUS_END_DECLS + +#endif /* DBUS_MESSAGE_INTERNAL_H */ diff --git a/src/dbus/dbus-message-private.h b/src/dbus/dbus-message-private.h new file mode 100644 index 0000000..c1e368f --- /dev/null +++ b/src/dbus/dbus-message-private.h @@ -0,0 +1,125 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-message-private.h header shared between dbus-message.c and dbus-message-util.c + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef DBUS_MESSAGE_PRIVATE_H +#define DBUS_MESSAGE_PRIVATE_H + +#include +#include +#include +#include +#include + +DBUS_BEGIN_DECLS + +/** + * @addtogroup DBusMessageInternals + * @{ + */ + +/** + * @typedef DBusMessageLoader + * + * The DBusMessageLoader object encapsulates the process of converting + * a byte stream into a series of DBusMessage. It buffers the incoming + * bytes as efficiently as possible, and generates a queue of + * messages. DBusMessageLoader is typically used as part of a + * DBusTransport implementation. The DBusTransport then hands off + * the loaded messages to a DBusConnection, making the messages + * visible to the application. + * + * @todo write tests for break-loader that a) randomly delete header + * fields and b) set string fields to zero-length and other funky + * values. + * + */ + +/** + * Implementation details of DBusMessageLoader. + * All members are private. + */ +struct DBusMessageLoader +{ + int refcount; /**< Reference count. */ + + DBusString data; /**< Buffered data */ + + DBusList *messages; /**< Complete messages. */ + + long max_message_size; /**< Maximum size of a message */ + + unsigned int buffer_outstanding : 1; /**< Someone is using the buffer to read */ + + unsigned int corrupted : 1; /**< We got broken data, and are no longer working */ + + DBusValidity corruption_reason; /**< why we were corrupted */ +}; + + +/** How many bits are in the changed_stamp used to validate iterators */ +#define CHANGED_STAMP_BITS 21 + +/** + * @brief Internals of DBusMessage + * + * Object representing a message received from or to be sent to + * another application. This is an opaque object, all members + * are private. + */ +struct DBusMessage +{ + DBusAtomic refcount; /**< Reference count */ + + DBusHeader header; /**< Header network data and associated cache */ + + DBusString body; /**< Body network data. */ + + char byte_order; /**< Message byte order. */ + + unsigned int locked : 1; /**< Message being sent, no modifications allowed. */ + +#ifndef DBUS_DISABLE_CHECKS + unsigned int in_cache : 1; /**< Has been "freed" since it's in the cache (this is a debug feature) */ +#endif + + DBusList *size_counters; /**< 0-N DBusCounter used to track message size. */ + long size_counter_delta; /**< Size we incremented the size counters by. */ + + dbus_uint32_t changed_stamp : CHANGED_STAMP_BITS; /**< Incremented when iterators are invalidated. */ + + DBusDataSlotList slot_list; /**< Data stored by allocated integer ID */ + +#ifndef DBUS_DISABLE_CHECKS + int generation; /**< _dbus_current_generation when message was created */ +#endif +}; + +dbus_bool_t _dbus_message_iter_get_args_valist (DBusMessageIter *iter, + DBusError *error, + int first_arg_type, + va_list var_args); + +/** @} */ + +DBUS_END_DECLS + +#endif /* DBUS_MESSAGE_H */ diff --git a/src/dbus/dbus-message-util.c b/src/dbus/dbus-message-util.c new file mode 100644 index 0000000..46cbe4e --- /dev/null +++ b/src/dbus/dbus-message-util.c @@ -0,0 +1,1320 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-message-util.c Would be in dbus-message.c, but only used by bus/tests + * + * Copyright (C) 2002, 2003, 2004, 2005 Red Hat Inc. + * Copyright (C) 2002, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-internals.h" +#include "dbus-test.h" +#include "dbus-message-private.h" +#include "dbus-marshal-recursive.h" +#include "dbus-string.h" + +/** + * @addtogroup DBusMessage + * @{ + */ + +#ifdef DBUS_BUILD_TESTS +/** + * Reads arguments from a message iterator given a variable argument + * list. Only arguments of basic type and arrays of fixed-length + * basic type may be read with this function. See + * dbus_message_get_args() for more details. + * + * @param iter the message iterator + * @param error error to be filled in on failure + * @param first_arg_type the first argument type + * @param ... location for first argument value, then list of type-location pairs + * @returns #FALSE if the error was set + */ +static dbus_bool_t +dbus_message_iter_get_args (DBusMessageIter *iter, + DBusError *error, + int first_arg_type, + ...) +{ + dbus_bool_t retval; + va_list var_args; + + _dbus_return_val_if_fail (iter != NULL, FALSE); + _dbus_return_val_if_error_is_set (error, FALSE); + + va_start (var_args, first_arg_type); + retval = _dbus_message_iter_get_args_valist (iter, error, first_arg_type, var_args); + va_end (var_args); + + return retval; +} +#endif /* DBUS_BUILD_TESTS */ + +/** @} */ + +#ifdef DBUS_BUILD_TESTS +#include "dbus-test.h" +#include "dbus-message-factory.h" +#include +#include + +static int validities_seen[DBUS_VALIDITY_LAST + _DBUS_NEGATIVE_VALIDITY_COUNT]; + +static void +reset_validities_seen (void) +{ + int i; + i = 0; + while (i < _DBUS_N_ELEMENTS (validities_seen)) + { + validities_seen[i] = 0; + ++i; + } +} + +static void +record_validity_seen (DBusValidity validity) +{ + validities_seen[validity + _DBUS_NEGATIVE_VALIDITY_COUNT] += 1; +} + +static void +print_validities_seen (dbus_bool_t not_seen) +{ + int i; + i = 0; + while (i < _DBUS_N_ELEMENTS (validities_seen)) + { + if ((i - _DBUS_NEGATIVE_VALIDITY_COUNT) == DBUS_VALIDITY_UNKNOWN || + (i - _DBUS_NEGATIVE_VALIDITY_COUNT) == DBUS_INVALID_FOR_UNKNOWN_REASON) + ; + else if ((not_seen && validities_seen[i] == 0) || + (!not_seen && validities_seen[i] > 0)) + printf ("validity %3d seen %d times\n", + i - _DBUS_NEGATIVE_VALIDITY_COUNT, + validities_seen[i]); + ++i; + } +} + +static void +check_memleaks (void) +{ + dbus_shutdown (); + + if (_dbus_get_malloc_blocks_outstanding () != 0) + { + _dbus_warn ("%d dbus_malloc blocks were not freed in %s\n", + _dbus_get_malloc_blocks_outstanding (), __FILE__); + _dbus_assert_not_reached ("memleaks"); + } +} + +static dbus_bool_t +check_have_valid_message (DBusMessageLoader *loader) +{ + DBusMessage *message; + dbus_bool_t retval; + + message = NULL; + retval = FALSE; + + if (_dbus_message_loader_get_is_corrupted (loader)) + { + _dbus_warn ("loader corrupted on message that was expected to be valid; invalid reason %d\n", + loader->corruption_reason); + goto failed; + } + + message = _dbus_message_loader_pop_message (loader); + if (message == NULL) + { + _dbus_warn ("didn't load message that was expected to be valid (message not popped)\n"); + goto failed; + } + + if (_dbus_string_get_length (&loader->data) > 0) + { + _dbus_warn ("had leftover bytes from expected-to-be-valid single message\n"); + goto failed; + } + +#if 0 + /* FIXME */ + /* Verify that we're able to properly deal with the message. + * For example, this would detect improper handling of messages + * in nonstandard byte order. + */ + if (!check_message_handling (message)) + goto failed; +#endif + + record_validity_seen (DBUS_VALID); + + retval = TRUE; + + failed: + if (message) + dbus_message_unref (message); + + return retval; +} + +static dbus_bool_t +check_invalid_message (DBusMessageLoader *loader, + DBusValidity expected_validity) +{ + dbus_bool_t retval; + + retval = FALSE; + + if (!_dbus_message_loader_get_is_corrupted (loader)) + { + _dbus_warn ("loader not corrupted on message that was expected to be invalid\n"); + goto failed; + } + + record_validity_seen (loader->corruption_reason); + + if (expected_validity != DBUS_INVALID_FOR_UNKNOWN_REASON && + loader->corruption_reason != expected_validity) + { + _dbus_warn ("expected message to be corrupted for reason %d and was corrupted for %d instead\n", + expected_validity, loader->corruption_reason); + goto failed; + } + + retval = TRUE; + + failed: + return retval; +} + +static dbus_bool_t +check_incomplete_message (DBusMessageLoader *loader) +{ + DBusMessage *message; + dbus_bool_t retval; + + message = NULL; + retval = FALSE; + + if (_dbus_message_loader_get_is_corrupted (loader)) + { + _dbus_warn ("loader corrupted on message that was expected to be valid (but incomplete), corruption reason %d\n", + loader->corruption_reason); + goto failed; + } + + message = _dbus_message_loader_pop_message (loader); + if (message != NULL) + { + _dbus_warn ("loaded message that was expected to be incomplete\n"); + goto failed; + } + + record_validity_seen (DBUS_VALID_BUT_INCOMPLETE); + retval = TRUE; + + failed: + if (message) + dbus_message_unref (message); + return retval; +} + +static dbus_bool_t +check_loader_results (DBusMessageLoader *loader, + DBusValidity expected_validity) +{ + if (!_dbus_message_loader_queue_messages (loader)) + _dbus_assert_not_reached ("no memory to queue messages"); + + if (expected_validity == DBUS_VALID) + return check_have_valid_message (loader); + else if (expected_validity == DBUS_VALID_BUT_INCOMPLETE) + return check_incomplete_message (loader); + else if (expected_validity == DBUS_VALIDITY_UNKNOWN) + { + /* here we just know we didn't segfault and that was the + * only test. Also, we record that we got coverage + * for the validity reason. + */ + if (_dbus_message_loader_get_is_corrupted (loader)) + record_validity_seen (loader->corruption_reason); + + return TRUE; + } + else + return check_invalid_message (loader, expected_validity); +} + +/** + * Loads the message in the given message file. + * + * @param filename filename to load + * @param data string to load message into + * @returns #TRUE if the message was loaded + */ +dbus_bool_t +dbus_internal_do_not_use_load_message_file (const DBusString *filename, + DBusString *data) +{ + dbus_bool_t retval; + DBusError error = DBUS_ERROR_INIT; + + retval = FALSE; + + _dbus_verbose ("Loading raw %s\n", _dbus_string_get_const_data (filename)); + if (!_dbus_file_get_contents (data, filename, &error)) + { + _dbus_warn ("Could not load message file %s: %s\n", + _dbus_string_get_const_data (filename), + error.message); + dbus_error_free (&error); + goto failed; + } + + retval = TRUE; + + failed: + + return retval; +} + +/** + * Tries loading the message in the given message file + * and verifies that DBusMessageLoader can handle it. + * + * @param filename filename to load + * @param expected_validity what the message has to be like to return #TRUE + * @returns #TRUE if the message has the expected validity + */ +dbus_bool_t +dbus_internal_do_not_use_try_message_file (const DBusString *filename, + DBusValidity expected_validity) +{ + DBusString data; + dbus_bool_t retval; + + retval = FALSE; + + if (!_dbus_string_init (&data)) + _dbus_assert_not_reached ("could not allocate string\n"); + + if (!dbus_internal_do_not_use_load_message_file (filename, &data)) + goto failed; + + retval = dbus_internal_do_not_use_try_message_data (&data, expected_validity); + + failed: + + if (!retval) + { + if (_dbus_string_get_length (&data) > 0) + _dbus_verbose_bytes_of_string (&data, 0, + _dbus_string_get_length (&data)); + + _dbus_warn ("Failed message loader test on %s\n", + _dbus_string_get_const_data (filename)); + } + + _dbus_string_free (&data); + + return retval; +} + +/** + * Tries loading the given message data. + * + * + * @param data the message data + * @param expected_validity what the message has to be like to return #TRUE + * @returns #TRUE if the message has the expected validity + */ +dbus_bool_t +dbus_internal_do_not_use_try_message_data (const DBusString *data, + DBusValidity expected_validity) +{ + DBusMessageLoader *loader; + dbus_bool_t retval; + int len; + int i; + + loader = NULL; + retval = FALSE; + + /* Write the data one byte at a time */ + + loader = _dbus_message_loader_new (); + + /* check some trivial loader functions */ + _dbus_message_loader_ref (loader); + _dbus_message_loader_unref (loader); + _dbus_message_loader_get_max_message_size (loader); + + len = _dbus_string_get_length (data); + for (i = 0; i < len; i++) + { + DBusString *buffer; + + _dbus_message_loader_get_buffer (loader, &buffer); + _dbus_string_append_byte (buffer, + _dbus_string_get_byte (data, i)); + _dbus_message_loader_return_buffer (loader, buffer, 1); + } + + if (!check_loader_results (loader, expected_validity)) + goto failed; + + _dbus_message_loader_unref (loader); + loader = NULL; + + /* Write the data all at once */ + + loader = _dbus_message_loader_new (); + + { + DBusString *buffer; + + _dbus_message_loader_get_buffer (loader, &buffer); + _dbus_string_copy (data, 0, buffer, + _dbus_string_get_length (buffer)); + _dbus_message_loader_return_buffer (loader, buffer, 1); + } + + if (!check_loader_results (loader, expected_validity)) + goto failed; + + _dbus_message_loader_unref (loader); + loader = NULL; + + /* Write the data 2 bytes at a time */ + + loader = _dbus_message_loader_new (); + + len = _dbus_string_get_length (data); + for (i = 0; i < len; i += 2) + { + DBusString *buffer; + + _dbus_message_loader_get_buffer (loader, &buffer); + _dbus_string_append_byte (buffer, + _dbus_string_get_byte (data, i)); + if ((i+1) < len) + _dbus_string_append_byte (buffer, + _dbus_string_get_byte (data, i+1)); + _dbus_message_loader_return_buffer (loader, buffer, 1); + } + + if (!check_loader_results (loader, expected_validity)) + goto failed; + + _dbus_message_loader_unref (loader); + loader = NULL; + + retval = TRUE; + + failed: + + if (loader) + _dbus_message_loader_unref (loader); + + return retval; +} + +static dbus_bool_t +process_test_subdir (const DBusString *test_base_dir, + const char *subdir, + DBusValidity expected_validity, + DBusForeachMessageFileFunc function, + void *user_data) +{ + DBusString test_directory; + DBusString filename; + DBusDirIter *dir; + dbus_bool_t retval; + DBusError error = DBUS_ERROR_INIT; + + 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"); + + 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; + } + + printf ("Testing %s:\n", subdir); + + 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 (&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 (&filename, ".message-raw")) + ; + else + { + if (_dbus_string_ends_with_c_str (&filename, ".message")) + { + _dbus_warn ("Could not load %s, message builder language no longer supported\n", + _dbus_string_get_const_data (&filename)); + } + + _dbus_verbose ("Skipping non-.message 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)); + + if (! (*function) (&full_path, + expected_validity, user_data)) + { + _dbus_string_free (&full_path); + goto failed; + } + else + _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; +} + +/** + * Runs the given function on every message file in the test suite. + * The function should return #FALSE on test failure or fatal error. + * + * @param test_data_dir root dir of the test suite data files (top_srcdir/test/data) + * @param func the function to run + * @param user_data data for function + * @returns #FALSE if there's a failure + */ +dbus_bool_t +dbus_internal_do_not_use_foreach_message_file (const char *test_data_dir, + DBusForeachMessageFileFunc func, + void *user_data) +{ + DBusString test_directory; + dbus_bool_t retval; + + retval = FALSE; + + _dbus_string_init_const (&test_directory, test_data_dir); + + if (!process_test_subdir (&test_directory, "valid-messages", + DBUS_VALID, func, user_data)) + goto failed; + + check_memleaks (); + + if (!process_test_subdir (&test_directory, "invalid-messages", + DBUS_INVALID_FOR_UNKNOWN_REASON, func, user_data)) + goto failed; + + check_memleaks (); + + if (!process_test_subdir (&test_directory, "incomplete-messages", + DBUS_VALID_BUT_INCOMPLETE, func, user_data)) + goto failed; + + check_memleaks (); + + retval = TRUE; + + failed: + + _dbus_string_free (&test_directory); + + return retval; +} + +#if 0 +#define GET_AND_CHECK(iter, typename, literal) \ + do { \ + if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_##typename) \ + _dbus_assert_not_reached ("got wrong argument type from message iter"); \ + dbus_message_iter_get_basic (&iter, &v_##typename); \ + if (v_##typename != literal) \ + _dbus_assert_not_reached ("got wrong value from message iter"); \ + } while (0) + +#define GET_AND_CHECK_STRCMP(iter, typename, literal) \ + do { \ + if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_##typename) \ + _dbus_assert_not_reached ("got wrong argument type from message iter"); \ + dbus_message_iter_get_basic (&iter, &v_##typename); \ + if (strcmp (v_##typename, literal) != 0) \ + _dbus_assert_not_reached ("got wrong value from message iter"); \ + } while (0) + +#define GET_AND_CHECK_AND_NEXT(iter, typename, literal) \ + do { \ + GET_AND_CHECK(iter, typename, literal); \ + if (!dbus_message_iter_next (&iter)) \ + _dbus_assert_not_reached ("failed to move iter to next"); \ + } while (0) + +#define GET_AND_CHECK_STRCMP_AND_NEXT(iter, typename, literal) \ + do { \ + GET_AND_CHECK_STRCMP(iter, typename, literal); \ + if (!dbus_message_iter_next (&iter)) \ + _dbus_assert_not_reached ("failed to move iter to next"); \ + } while (0) + +static void +message_iter_test (DBusMessage *message) +{ + DBusMessageIter iter, array, array2; + const char *v_STRING; + double v_DOUBLE; + dbus_int16_t v_INT16; + dbus_uint16_t v_UINT16; + dbus_int32_t v_INT32; + dbus_uint32_t v_UINT32; +#ifdef DBUS_HAVE_INT64 + dbus_int64_t v_INT64; + dbus_uint64_t v_UINT64; +#endif + unsigned char v_BYTE; + dbus_bool_t v_BOOLEAN; + + const dbus_int32_t *our_int_array; + int len; + + dbus_message_iter_init (message, &iter); + + GET_AND_CHECK_STRCMP_AND_NEXT (iter, STRING, "Test string"); + GET_AND_CHECK_AND_NEXT (iter, INT32, -0x12345678); + GET_AND_CHECK_AND_NEXT (iter, UINT32, 0xedd1e); + GET_AND_CHECK_AND_NEXT (iter, DOUBLE, 3.14159); + + if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY) + _dbus_assert_not_reached ("Argument type not an array"); + + if (dbus_message_iter_get_element_type (&iter) != DBUS_TYPE_DOUBLE) + _dbus_assert_not_reached ("Array type not double"); + + dbus_message_iter_recurse (&iter, &array); + + GET_AND_CHECK_AND_NEXT (array, DOUBLE, 1.5); + GET_AND_CHECK (array, DOUBLE, 2.5); + + if (dbus_message_iter_next (&array)) + _dbus_assert_not_reached ("Didn't reach end of array"); + + if (!dbus_message_iter_next (&iter)) + _dbus_assert_not_reached ("Reached end of arguments"); + + GET_AND_CHECK_AND_NEXT (iter, BYTE, 0xF0); + + if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY) + _dbus_assert_not_reached ("no array"); + + if (dbus_message_iter_get_element_type (&iter) != DBUS_TYPE_INT32) + _dbus_assert_not_reached ("Array type not int32"); + + /* Empty array */ + dbus_message_iter_recurse (&iter, &array); + + if (dbus_message_iter_next (&array)) + _dbus_assert_not_reached ("Didn't reach end of array"); + + if (!dbus_message_iter_next (&iter)) + _dbus_assert_not_reached ("Reached end of arguments"); + + GET_AND_CHECK (iter, BYTE, 0xF0); + + if (dbus_message_iter_next (&iter)) + _dbus_assert_not_reached ("Didn't reach end of arguments"); +} +#endif + +static void +verify_test_message (DBusMessage *message) +{ + DBusMessageIter iter; + DBusError error = DBUS_ERROR_INIT; + dbus_int16_t our_int16; + dbus_uint16_t our_uint16; + dbus_int32_t our_int; + dbus_uint32_t our_uint; + const char *our_str; + double our_double; + double v_DOUBLE; + dbus_bool_t our_bool; + unsigned char our_byte_1, our_byte_2; + const dbus_uint32_t *our_uint32_array = (void*)0xdeadbeef; + int our_uint32_array_len; + dbus_int32_t *our_int32_array = (void*)0xdeadbeef; + int our_int32_array_len; +#ifdef DBUS_HAVE_INT64 + dbus_int64_t our_int64; + dbus_uint64_t our_uint64; + dbus_int64_t *our_uint64_array = (void*)0xdeadbeef; + int our_uint64_array_len; + const dbus_int64_t *our_int64_array = (void*)0xdeadbeef; + int our_int64_array_len; +#endif + const double *our_double_array = (void*)0xdeadbeef; + int our_double_array_len; + const unsigned char *our_byte_array = (void*)0xdeadbeef; + int our_byte_array_len; + const dbus_bool_t *our_boolean_array = (void*)0xdeadbeef; + int our_boolean_array_len; + char **our_string_array; + int our_string_array_len; + + dbus_message_iter_init (message, &iter); + + if (!dbus_message_iter_get_args (&iter, &error, + DBUS_TYPE_INT16, &our_int16, + DBUS_TYPE_UINT16, &our_uint16, + DBUS_TYPE_INT32, &our_int, + DBUS_TYPE_UINT32, &our_uint, +#ifdef DBUS_HAVE_INT64 + DBUS_TYPE_INT64, &our_int64, + DBUS_TYPE_UINT64, &our_uint64, +#endif + DBUS_TYPE_STRING, &our_str, + DBUS_TYPE_DOUBLE, &our_double, + DBUS_TYPE_BOOLEAN, &our_bool, + DBUS_TYPE_BYTE, &our_byte_1, + DBUS_TYPE_BYTE, &our_byte_2, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, + &our_uint32_array, &our_uint32_array_len, + DBUS_TYPE_ARRAY, DBUS_TYPE_INT32, + &our_int32_array, &our_int32_array_len, +#ifdef DBUS_HAVE_INT64 + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT64, + &our_uint64_array, &our_uint64_array_len, + DBUS_TYPE_ARRAY, DBUS_TYPE_INT64, + &our_int64_array, &our_int64_array_len, +#endif + DBUS_TYPE_ARRAY, DBUS_TYPE_DOUBLE, + &our_double_array, &our_double_array_len, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + &our_byte_array, &our_byte_array_len, + DBUS_TYPE_ARRAY, DBUS_TYPE_BOOLEAN, + &our_boolean_array, &our_boolean_array_len, + DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, + &our_string_array, &our_string_array_len, + 0)) + { + _dbus_warn ("error: %s - %s\n", error.name, + (error.message != NULL) ? error.message : "no message"); + _dbus_assert_not_reached ("Could not get arguments"); + } + + if (our_int16 != -0x123) + _dbus_assert_not_reached ("16-bit integers differ!"); + + if (our_uint16 != 0x123) + _dbus_assert_not_reached ("16-bit uints differ!"); + + if (our_int != -0x12345678) + _dbus_assert_not_reached ("integers differ!"); + + if (our_uint != 0x12300042) + _dbus_assert_not_reached ("uints differ!"); + +#ifdef DBUS_HAVE_INT64 + if (our_int64 != DBUS_INT64_CONSTANT (-0x123456789abcd)) + _dbus_assert_not_reached ("64-bit integers differ!"); + if (our_uint64 != DBUS_UINT64_CONSTANT (0x123456789abcd)) + _dbus_assert_not_reached ("64-bit unsigned integers differ!"); +#endif + + v_DOUBLE = 3.14159; + if (! _DBUS_DOUBLES_BITWISE_EQUAL (our_double, v_DOUBLE)) + _dbus_assert_not_reached ("doubles differ!"); + + if (strcmp (our_str, "Test string") != 0) + _dbus_assert_not_reached ("strings differ!"); + + if (!our_bool) + _dbus_assert_not_reached ("booleans differ"); + + if (our_byte_1 != 42) + _dbus_assert_not_reached ("bytes differ!"); + + if (our_byte_2 != 24) + _dbus_assert_not_reached ("bytes differ!"); + + if (our_uint32_array_len != 4 || + our_uint32_array[0] != 0x12345678 || + our_uint32_array[1] != 0x23456781 || + our_uint32_array[2] != 0x34567812 || + our_uint32_array[3] != 0x45678123) + _dbus_assert_not_reached ("uint array differs"); + + if (our_int32_array_len != 4 || + our_int32_array[0] != 0x12345678 || + our_int32_array[1] != -0x23456781 || + our_int32_array[2] != 0x34567812 || + our_int32_array[3] != -0x45678123) + _dbus_assert_not_reached ("int array differs"); + +#ifdef DBUS_HAVE_INT64 + if (our_uint64_array_len != 4 || + our_uint64_array[0] != 0x12345678 || + our_uint64_array[1] != 0x23456781 || + our_uint64_array[2] != 0x34567812 || + our_uint64_array[3] != 0x45678123) + _dbus_assert_not_reached ("uint64 array differs"); + + if (our_int64_array_len != 4 || + our_int64_array[0] != 0x12345678 || + our_int64_array[1] != -0x23456781 || + our_int64_array[2] != 0x34567812 || + our_int64_array[3] != -0x45678123) + _dbus_assert_not_reached ("int64 array differs"); +#endif /* DBUS_HAVE_INT64 */ + + if (our_double_array_len != 3) + _dbus_assert_not_reached ("double array had wrong length"); + + /* On all IEEE machines (i.e. everything sane) exact equality + * should be preserved over the wire + */ + v_DOUBLE = 0.1234; + if (! _DBUS_DOUBLES_BITWISE_EQUAL (our_double_array[0], v_DOUBLE)) + _dbus_assert_not_reached ("double array had wrong values"); + v_DOUBLE = 9876.54321; + if (! _DBUS_DOUBLES_BITWISE_EQUAL (our_double_array[1], v_DOUBLE)) + _dbus_assert_not_reached ("double array had wrong values"); + v_DOUBLE = -300.0; + if (! _DBUS_DOUBLES_BITWISE_EQUAL (our_double_array[2], v_DOUBLE)) + _dbus_assert_not_reached ("double array had wrong values"); + + if (our_byte_array_len != 4) + _dbus_assert_not_reached ("byte array had wrong length"); + + if (our_byte_array[0] != 'a' || + our_byte_array[1] != 'b' || + our_byte_array[2] != 'c' || + our_byte_array[3] != 234) + _dbus_assert_not_reached ("byte array had wrong values"); + + if (our_boolean_array_len != 5) + _dbus_assert_not_reached ("bool array had wrong length"); + + if (our_boolean_array[0] != TRUE || + our_boolean_array[1] != FALSE || + our_boolean_array[2] != TRUE || + our_boolean_array[3] != TRUE || + our_boolean_array[4] != FALSE) + _dbus_assert_not_reached ("bool array had wrong values"); + + if (our_string_array_len != 4) + _dbus_assert_not_reached ("string array was wrong length"); + + if (strcmp (our_string_array[0], "Foo") != 0 || + strcmp (our_string_array[1], "bar") != 0 || + strcmp (our_string_array[2], "") != 0 || + strcmp (our_string_array[3], "woo woo woo woo") != 0) + _dbus_assert_not_reached ("string array had wrong values"); + + dbus_free_string_array (our_string_array); + + if (dbus_message_iter_next (&iter)) + _dbus_assert_not_reached ("Didn't reach end of arguments"); +} + +/** + * @ingroup DBusMessageInternals + * Unit test for DBusMessage. + * + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_message_test (const char *test_data_dir) +{ + DBusMessage *message; + DBusMessageLoader *loader; + int i; + const char *data; + DBusMessage *copy; + const char *name1; + const char *name2; + const dbus_uint32_t our_uint32_array[] = + { 0x12345678, 0x23456781, 0x34567812, 0x45678123 }; + const dbus_int32_t our_int32_array[] = + { 0x12345678, -0x23456781, 0x34567812, -0x45678123 }; + const dbus_uint32_t *v_ARRAY_UINT32 = our_uint32_array; + const dbus_int32_t *v_ARRAY_INT32 = our_int32_array; +#ifdef DBUS_HAVE_INT64 + const dbus_uint64_t our_uint64_array[] = + { 0x12345678, 0x23456781, 0x34567812, 0x45678123 }; + const dbus_int64_t our_int64_array[] = + { 0x12345678, -0x23456781, 0x34567812, -0x45678123 }; + const dbus_uint64_t *v_ARRAY_UINT64 = our_uint64_array; + const dbus_int64_t *v_ARRAY_INT64 = our_int64_array; +#endif + const char *our_string_array[] = { "Foo", "bar", "", "woo woo woo woo" }; + const char **v_ARRAY_STRING = our_string_array; + const double our_double_array[] = { 0.1234, 9876.54321, -300.0 }; + const double *v_ARRAY_DOUBLE = our_double_array; + const unsigned char our_byte_array[] = { 'a', 'b', 'c', 234 }; + const unsigned char *v_ARRAY_BYTE = our_byte_array; + const dbus_bool_t our_boolean_array[] = { TRUE, FALSE, TRUE, TRUE, FALSE }; + const dbus_bool_t *v_ARRAY_BOOLEAN = our_boolean_array; + char sig[64]; + const char *s; + const char *v_STRING; + double v_DOUBLE; + dbus_int16_t v_INT16; + dbus_uint16_t v_UINT16; + dbus_int32_t v_INT32; + dbus_uint32_t v_UINT32; +#ifdef DBUS_HAVE_INT64 + dbus_int64_t v_INT64; + dbus_uint64_t v_UINT64; +#endif + unsigned char v_BYTE; + unsigned char v2_BYTE; + dbus_bool_t v_BOOLEAN; + + message = dbus_message_new_method_call ("org.freedesktop.DBus.TestService", + "/org/freedesktop/TestPath", + "Foo.TestInterface", + "TestMethod"); + _dbus_assert (dbus_message_has_destination (message, "org.freedesktop.DBus.TestService")); + _dbus_assert (dbus_message_is_method_call (message, "Foo.TestInterface", + "TestMethod")); + _dbus_assert (strcmp (dbus_message_get_path (message), + "/org/freedesktop/TestPath") == 0); + dbus_message_set_serial (message, 1234); + + /* string length including nul byte not a multiple of 4 */ + if (!dbus_message_set_sender (message, "org.foo.bar1")) + _dbus_assert_not_reached ("out of memory"); + + _dbus_assert (dbus_message_has_sender (message, "org.foo.bar1")); + dbus_message_set_reply_serial (message, 5678); + + _dbus_verbose_bytes_of_string (&message->header.data, 0, + _dbus_string_get_length (&message->header.data)); + _dbus_verbose_bytes_of_string (&message->body, 0, + _dbus_string_get_length (&message->body)); + + if (!dbus_message_set_sender (message, NULL)) + _dbus_assert_not_reached ("out of memory"); + + + _dbus_verbose_bytes_of_string (&message->header.data, 0, + _dbus_string_get_length (&message->header.data)); + _dbus_verbose_bytes_of_string (&message->body, 0, + _dbus_string_get_length (&message->body)); + + + _dbus_assert (!dbus_message_has_sender (message, "org.foo.bar1")); + _dbus_assert (dbus_message_get_serial (message) == 1234); + _dbus_assert (dbus_message_get_reply_serial (message) == 5678); + _dbus_assert (dbus_message_has_destination (message, "org.freedesktop.DBus.TestService")); + + _dbus_assert (dbus_message_get_no_reply (message) == FALSE); + dbus_message_set_no_reply (message, TRUE); + _dbus_assert (dbus_message_get_no_reply (message) == TRUE); + dbus_message_set_no_reply (message, FALSE); + _dbus_assert (dbus_message_get_no_reply (message) == FALSE); + + /* Set/get some header fields */ + + if (!dbus_message_set_path (message, "/foo")) + _dbus_assert_not_reached ("out of memory"); + _dbus_assert (strcmp (dbus_message_get_path (message), + "/foo") == 0); + + if (!dbus_message_set_interface (message, "org.Foo")) + _dbus_assert_not_reached ("out of memory"); + _dbus_assert (strcmp (dbus_message_get_interface (message), + "org.Foo") == 0); + + if (!dbus_message_set_member (message, "Bar")) + _dbus_assert_not_reached ("out of memory"); + _dbus_assert (strcmp (dbus_message_get_member (message), + "Bar") == 0); + + /* Set/get them with longer values */ + if (!dbus_message_set_path (message, "/foo/bar")) + _dbus_assert_not_reached ("out of memory"); + _dbus_assert (strcmp (dbus_message_get_path (message), + "/foo/bar") == 0); + + if (!dbus_message_set_interface (message, "org.Foo.Bar")) + _dbus_assert_not_reached ("out of memory"); + _dbus_assert (strcmp (dbus_message_get_interface (message), + "org.Foo.Bar") == 0); + + if (!dbus_message_set_member (message, "BarFoo")) + _dbus_assert_not_reached ("out of memory"); + _dbus_assert (strcmp (dbus_message_get_member (message), + "BarFoo") == 0); + + /* Realloc shorter again */ + + if (!dbus_message_set_path (message, "/foo")) + _dbus_assert_not_reached ("out of memory"); + _dbus_assert (strcmp (dbus_message_get_path (message), + "/foo") == 0); + + if (!dbus_message_set_interface (message, "org.Foo")) + _dbus_assert_not_reached ("out of memory"); + _dbus_assert (strcmp (dbus_message_get_interface (message), + "org.Foo") == 0); + + if (!dbus_message_set_member (message, "Bar")) + _dbus_assert_not_reached ("out of memory"); + _dbus_assert (strcmp (dbus_message_get_member (message), + "Bar") == 0); + + dbus_message_unref (message); + + /* Test the vararg functions */ + message = dbus_message_new_method_call ("org.freedesktop.DBus.TestService", + "/org/freedesktop/TestPath", + "Foo.TestInterface", + "TestMethod"); + dbus_message_set_serial (message, 1); + dbus_message_set_reply_serial (message, 5678); + + v_INT16 = -0x123; + v_UINT16 = 0x123; + v_INT32 = -0x12345678; + v_UINT32 = 0x12300042; +#ifdef DBUS_HAVE_INT64 + v_INT64 = DBUS_INT64_CONSTANT (-0x123456789abcd); + v_UINT64 = DBUS_UINT64_CONSTANT (0x123456789abcd); +#endif + v_STRING = "Test string"; + v_DOUBLE = 3.14159; + v_BOOLEAN = TRUE; + v_BYTE = 42; + v2_BYTE = 24; + + dbus_message_append_args (message, + DBUS_TYPE_INT16, &v_INT16, + DBUS_TYPE_UINT16, &v_UINT16, + DBUS_TYPE_INT32, &v_INT32, + DBUS_TYPE_UINT32, &v_UINT32, +#ifdef DBUS_HAVE_INT64 + DBUS_TYPE_INT64, &v_INT64, + DBUS_TYPE_UINT64, &v_UINT64, +#endif + DBUS_TYPE_STRING, &v_STRING, + DBUS_TYPE_DOUBLE, &v_DOUBLE, + DBUS_TYPE_BOOLEAN, &v_BOOLEAN, + DBUS_TYPE_BYTE, &v_BYTE, + DBUS_TYPE_BYTE, &v2_BYTE, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &v_ARRAY_UINT32, + _DBUS_N_ELEMENTS (our_uint32_array), + DBUS_TYPE_ARRAY, DBUS_TYPE_INT32, &v_ARRAY_INT32, + _DBUS_N_ELEMENTS (our_int32_array), +#ifdef DBUS_HAVE_INT64 + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT64, &v_ARRAY_UINT64, + _DBUS_N_ELEMENTS (our_uint64_array), + DBUS_TYPE_ARRAY, DBUS_TYPE_INT64, &v_ARRAY_INT64, + _DBUS_N_ELEMENTS (our_int64_array), +#endif + DBUS_TYPE_ARRAY, DBUS_TYPE_DOUBLE, &v_ARRAY_DOUBLE, + _DBUS_N_ELEMENTS (our_double_array), + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &v_ARRAY_BYTE, + _DBUS_N_ELEMENTS (our_byte_array), + DBUS_TYPE_ARRAY, DBUS_TYPE_BOOLEAN, &v_ARRAY_BOOLEAN, + _DBUS_N_ELEMENTS (our_boolean_array), + DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &v_ARRAY_STRING, + _DBUS_N_ELEMENTS (our_string_array), + DBUS_TYPE_INVALID); + + i = 0; + sig[i++] = DBUS_TYPE_INT16; + sig[i++] = DBUS_TYPE_UINT16; + sig[i++] = DBUS_TYPE_INT32; + sig[i++] = DBUS_TYPE_UINT32; +#ifdef DBUS_HAVE_INT64 + sig[i++] = DBUS_TYPE_INT64; + sig[i++] = DBUS_TYPE_UINT64; +#endif + sig[i++] = DBUS_TYPE_STRING; + sig[i++] = DBUS_TYPE_DOUBLE; + sig[i++] = DBUS_TYPE_BOOLEAN; + sig[i++] = DBUS_TYPE_BYTE; + sig[i++] = DBUS_TYPE_BYTE; + sig[i++] = DBUS_TYPE_ARRAY; + sig[i++] = DBUS_TYPE_UINT32; + sig[i++] = DBUS_TYPE_ARRAY; + sig[i++] = DBUS_TYPE_INT32; +#ifdef DBUS_HAVE_INT64 + sig[i++] = DBUS_TYPE_ARRAY; + sig[i++] = DBUS_TYPE_UINT64; + sig[i++] = DBUS_TYPE_ARRAY; + sig[i++] = DBUS_TYPE_INT64; +#endif + sig[i++] = DBUS_TYPE_ARRAY; + sig[i++] = DBUS_TYPE_DOUBLE; + sig[i++] = DBUS_TYPE_ARRAY; + sig[i++] = DBUS_TYPE_BYTE; + sig[i++] = DBUS_TYPE_ARRAY; + sig[i++] = DBUS_TYPE_BOOLEAN; + sig[i++] = DBUS_TYPE_ARRAY; + sig[i++] = DBUS_TYPE_STRING; + sig[i++] = DBUS_TYPE_INVALID; + + _dbus_assert (i < (int) _DBUS_N_ELEMENTS (sig)); + + _dbus_verbose ("HEADER\n"); + _dbus_verbose_bytes_of_string (&message->header.data, 0, + _dbus_string_get_length (&message->header.data)); + _dbus_verbose ("BODY\n"); + _dbus_verbose_bytes_of_string (&message->body, 0, + _dbus_string_get_length (&message->body)); + + _dbus_verbose ("Signature expected \"%s\" actual \"%s\"\n", + sig, dbus_message_get_signature (message)); + + s = dbus_message_get_signature (message); + + _dbus_assert (dbus_message_has_signature (message, sig)); + _dbus_assert (strcmp (s, sig) == 0); + + verify_test_message (message); + + copy = dbus_message_copy (message); + + _dbus_assert (dbus_message_get_reply_serial (message) == + dbus_message_get_reply_serial (copy)); + _dbus_assert (message->header.padding == copy->header.padding); + + _dbus_assert (_dbus_string_get_length (&message->header.data) == + _dbus_string_get_length (©->header.data)); + + _dbus_assert (_dbus_string_get_length (&message->body) == + _dbus_string_get_length (©->body)); + + verify_test_message (copy); + + name1 = dbus_message_get_interface (message); + name2 = dbus_message_get_interface (copy); + + _dbus_assert (strcmp (name1, name2) == 0); + + name1 = dbus_message_get_member (message); + name2 = dbus_message_get_member (copy); + + _dbus_assert (strcmp (name1, name2) == 0); + + dbus_message_unref (copy); + + /* Message loader test */ + dbus_message_lock (message); + loader = _dbus_message_loader_new (); + + /* check ref/unref */ + _dbus_message_loader_ref (loader); + _dbus_message_loader_unref (loader); + + /* Write the header data one byte at a time */ + data = _dbus_string_get_const_data (&message->header.data); + for (i = 0; i < _dbus_string_get_length (&message->header.data); i++) + { + DBusString *buffer; + + _dbus_message_loader_get_buffer (loader, &buffer); + _dbus_string_append_byte (buffer, data[i]); + _dbus_message_loader_return_buffer (loader, buffer, 1); + } + + /* Write the body data one byte at a time */ + data = _dbus_string_get_const_data (&message->body); + for (i = 0; i < _dbus_string_get_length (&message->body); i++) + { + DBusString *buffer; + + _dbus_message_loader_get_buffer (loader, &buffer); + _dbus_string_append_byte (buffer, data[i]); + _dbus_message_loader_return_buffer (loader, buffer, 1); + } + + dbus_message_unref (message); + + /* Now pop back the message */ + if (!_dbus_message_loader_queue_messages (loader)) + _dbus_assert_not_reached ("no memory to queue messages"); + + if (_dbus_message_loader_get_is_corrupted (loader)) + _dbus_assert_not_reached ("message loader corrupted"); + + message = _dbus_message_loader_pop_message (loader); + if (!message) + _dbus_assert_not_reached ("received a NULL message"); + + if (dbus_message_get_reply_serial (message) != 5678) + _dbus_assert_not_reached ("reply serial fields differ"); + + verify_test_message (message); + + { + /* Marshal and demarshal the message. */ + + DBusMessage *message2; + DBusError error = DBUS_ERROR_INIT; + char *marshalled = NULL; + int len = 0; + char garbage_header[DBUS_MINIMUM_HEADER_SIZE] = "xxx"; + + if (!dbus_message_marshal (message, &marshalled, &len)) + _dbus_assert_not_reached ("failed to marshal message"); + + _dbus_assert (len != 0); + _dbus_assert (marshalled != NULL); + + _dbus_assert (dbus_message_demarshal_bytes_needed (marshalled, len) == len); + message2 = dbus_message_demarshal (marshalled, len, &error); + + _dbus_assert (message2 != NULL); + _dbus_assert (!dbus_error_is_set (&error)); + verify_test_message (message2); + + dbus_message_unref (message2); + dbus_free (marshalled); + + /* Demarshal invalid message. */ + + message2 = dbus_message_demarshal ("invalid", 7, &error); + _dbus_assert (message2 == NULL); + _dbus_assert (dbus_error_is_set (&error)); + dbus_error_free (&error); + + /* Demarshal invalid (empty) message. */ + + message2 = dbus_message_demarshal ("", 0, &error); + _dbus_assert (message2 == NULL); + _dbus_assert (dbus_error_is_set (&error)); + dbus_error_free (&error); + + /* Bytes needed to demarshal empty message: 0 (more) */ + + _dbus_assert (dbus_message_demarshal_bytes_needed ("", 0) == 0); + + /* Bytes needed to demarshal invalid message: -1 (error). */ + + _dbus_assert (dbus_message_demarshal_bytes_needed (garbage_header, DBUS_MINIMUM_HEADER_SIZE) == -1); + } + + dbus_message_unref (message); + _dbus_message_loader_unref (loader); + + check_memleaks (); + + /* Load all the sample messages from the message factory */ + { + DBusMessageDataIter diter; + DBusMessageData mdata; + int count; + + reset_validities_seen (); + + count = 0; + _dbus_message_data_iter_init (&diter); + + while (_dbus_message_data_iter_get_and_next (&diter, + &mdata)) + { + if (!dbus_internal_do_not_use_try_message_data (&mdata.data, + mdata.expected_validity)) + { + _dbus_warn ("expected validity %d and did not get it\n", + mdata.expected_validity); + _dbus_assert_not_reached ("message data failed"); + } + + _dbus_message_data_free (&mdata); + + count += 1; + } + + printf ("%d sample messages tested\n", count); + + print_validities_seen (FALSE); + print_validities_seen (TRUE); + } + + check_memleaks (); + + /* Now load every message in test_data_dir if we have one */ + if (test_data_dir == NULL) + return TRUE; + + return dbus_internal_do_not_use_foreach_message_file (test_data_dir, + (DBusForeachMessageFileFunc) + dbus_internal_do_not_use_try_message_file, + NULL); +} + +#endif /* DBUS_BUILD_TESTS */ diff --git a/src/dbus/dbus-message.c b/src/dbus/dbus-message.c new file mode 100644 index 0000000..edae425 --- /dev/null +++ b/src/dbus/dbus-message.c @@ -0,0 +1,4083 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-message.c DBusMessage object + * + * Copyright (C) 2002, 2003, 2004, 2005 Red Hat Inc. + * Copyright (C) 2002, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-internals.h" +#include "dbus-marshal-recursive.h" +#include "dbus-marshal-validate.h" +#include "dbus-marshal-byteswap.h" +#include "dbus-marshal-header.h" +#include "dbus-signature.h" +#include "dbus-message-private.h" +#include "dbus-object-tree.h" +#include "dbus-memory.h" +#include "dbus-list.h" +#include "dbus-threads-internal.h" +#include + +static void dbus_message_finalize (DBusMessage *message); + +/** + * @defgroup DBusMessageInternals DBusMessage implementation details + * @ingroup DBusInternals + * @brief DBusMessage private implementation details. + * + * The guts of DBusMessage and its methods. + * + * @{ + */ + +/* Not thread locked, but strictly const/read-only so should be OK + */ +/** An static string representing an empty signature */ +_DBUS_STRING_DEFINE_STATIC(_dbus_empty_signature_str, ""); + +/* these have wacky values to help trap uninitialized iterators; + * but has to fit in 3 bits + */ +enum { + DBUS_MESSAGE_ITER_TYPE_READER = 3, + DBUS_MESSAGE_ITER_TYPE_WRITER = 7 +}; + +/** typedef for internals of message iterator */ +typedef struct DBusMessageRealIter DBusMessageRealIter; + +/** + * @brief Internals of DBusMessageIter + * + * Object representing a position in a message. All fields are internal. + */ +struct DBusMessageRealIter +{ + DBusMessage *message; /**< Message used */ + dbus_uint32_t changed_stamp : CHANGED_STAMP_BITS; /**< stamp to detect invalid iters */ + dbus_uint32_t iter_type : 3; /**< whether this is a reader or writer iter */ + dbus_uint32_t sig_refcount : 8; /**< depth of open_signature() */ + union + { + DBusTypeWriter writer; /**< writer */ + DBusTypeReader reader; /**< reader */ + } u; /**< the type writer or reader that does all the work */ +}; + +static void +get_const_signature (DBusHeader *header, + const DBusString **type_str_p, + int *type_pos_p) +{ + if (_dbus_header_get_field_raw (header, + DBUS_HEADER_FIELD_SIGNATURE, + type_str_p, + type_pos_p)) + { + *type_pos_p += 1; /* skip the signature length which is 1 byte */ + } + else + { + *type_str_p = &_dbus_empty_signature_str; + *type_pos_p = 0; + } +} + +/** + * Swaps the message to compiler byte order if required + * + * @param message the message + */ +static void +_dbus_message_byteswap (DBusMessage *message) +{ + const DBusString *type_str; + int type_pos; + + if (message->byte_order == DBUS_COMPILER_BYTE_ORDER) + return; + + _dbus_verbose ("Swapping message into compiler byte order\n"); + + get_const_signature (&message->header, &type_str, &type_pos); + + _dbus_marshal_byteswap (type_str, type_pos, + message->byte_order, + DBUS_COMPILER_BYTE_ORDER, + &message->body, 0); + + message->byte_order = DBUS_COMPILER_BYTE_ORDER; + + _dbus_header_byteswap (&message->header, DBUS_COMPILER_BYTE_ORDER); +} + +/** byte-swap the message if it doesn't match our byte order. + * Called only when we need the message in our own byte order, + * normally when reading arrays of integers or doubles. + * Otherwise should not be called since it would do needless + * work. + */ +#define ensure_byte_order(message) \ + if (message->byte_order != DBUS_COMPILER_BYTE_ORDER) \ + _dbus_message_byteswap (message) + +/** + * Gets the data to be sent over the network for this message. + * The header and then the body should be written out. + * This function is guaranteed to always return the same + * data once a message is locked (with dbus_message_lock()). + * + * @param message the message. + * @param header return location for message header data. + * @param body return location for message body data. + */ +void +_dbus_message_get_network_data (DBusMessage *message, + const DBusString **header, + const DBusString **body) +{ + _dbus_assert (message->locked); + + *header = &message->header.data; + *body = &message->body; +} + +/** + * Sets the serial number of a message. + * This can only be done once on a message. + * + * DBusConnection will automatically set the serial to an appropriate value + * when the message is sent; this function is only needed when encapsulating + * messages in another protocol, or otherwise bypassing DBusConnection. + * + * @param message the message + * @param serial the serial + */ +void +dbus_message_set_serial (DBusMessage *message, + dbus_uint32_t serial) +{ + _dbus_return_if_fail (message != NULL); + _dbus_return_if_fail (!message->locked); + + _dbus_header_set_serial (&message->header, serial); +} + +/** + * Adds a counter to be incremented immediately with the + * size of this message, and decremented by the size + * of this message when this message if finalized. + * The link contains a counter with its refcount already + * incremented, but the counter itself not incremented. + * Ownership of link and counter refcount is passed to + * the message. + * + * @param message the message + * @param link link with counter as data + */ +void +_dbus_message_add_size_counter_link (DBusMessage *message, + DBusList *link) +{ + /* right now we don't recompute the delta when message + * size changes, and that's OK for current purposes + * I think, but could be important to change later. + * Do recompute it whenever there are no outstanding counters, + * since it's basically free. + */ + if (message->size_counters == NULL) + { + message->size_counter_delta = + _dbus_string_get_length (&message->header.data) + + _dbus_string_get_length (&message->body); + +#if 0 + _dbus_verbose ("message has size %ld\n", + message->size_counter_delta); +#endif + } + + _dbus_list_append_link (&message->size_counters, link); + + _dbus_counter_adjust (link->data, message->size_counter_delta); +} + +/** + * Adds a counter to be incremented immediately with the + * size of this message, and decremented by the size + * of this message when this message if finalized. + * + * @param message the message + * @param counter the counter + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_message_add_size_counter (DBusMessage *message, + DBusCounter *counter) +{ + DBusList *link; + + link = _dbus_list_alloc_link (counter); + if (link == NULL) + return FALSE; + + _dbus_counter_ref (counter); + _dbus_message_add_size_counter_link (message, link); + + return TRUE; +} + +/** + * Removes a counter tracking the size of this message, and decrements + * the counter by the size of this message. + * + * @param message the message + * @param link_return return the link used + * @param counter the counter + */ +void +_dbus_message_remove_size_counter (DBusMessage *message, + DBusCounter *counter, + DBusList **link_return) +{ + DBusList *link; + + link = _dbus_list_find_last (&message->size_counters, + counter); + _dbus_assert (link != NULL); + + _dbus_list_unlink (&message->size_counters, + link); + if (link_return) + *link_return = link; + else + _dbus_list_free_link (link); + + _dbus_counter_adjust (counter, - message->size_counter_delta); + + _dbus_counter_unref (counter); +} + +/** + * Locks a message. Allows checking that applications don't keep a + * reference to a message in the outgoing queue and change it + * underneath us. Messages are locked when they enter the outgoing + * queue (dbus_connection_send_message()), and the library complains + * if the message is modified while locked. This function may also + * called externally, for applications wrapping D-Bus in another protocol. + * + * @param message the message to lock. + */ +void +dbus_message_lock (DBusMessage *message) +{ + if (!message->locked) + { + _dbus_header_update_lengths (&message->header, + _dbus_string_get_length (&message->body)); + + /* must have a signature if you have a body */ + _dbus_assert (_dbus_string_get_length (&message->body) == 0 || + dbus_message_get_signature (message) != NULL); + + message->locked = TRUE; + } +} + +static dbus_bool_t +set_or_delete_string_field (DBusMessage *message, + int field, + int typecode, + const char *value) +{ + if (value == NULL) + return _dbus_header_delete_field (&message->header, field); + else + return _dbus_header_set_field_basic (&message->header, + field, + typecode, + &value); +} + +#if 0 +/* Probably we don't need to use this */ +/** + * Sets the signature of the message, i.e. the arguments in the + * message payload. The signature includes only "in" arguments for + * #DBUS_MESSAGE_TYPE_METHOD_CALL and only "out" arguments for + * #DBUS_MESSAGE_TYPE_METHOD_RETURN, so is slightly different from + * what you might expect (it does not include the signature of the + * entire C++-style method). + * + * The signature is a string made up of type codes such as + * #DBUS_TYPE_INT32. The string is terminated with nul (nul is also + * the value of #DBUS_TYPE_INVALID). The macros such as + * #DBUS_TYPE_INT32 evaluate to integers; to assemble a signature you + * may find it useful to use the string forms, such as + * #DBUS_TYPE_INT32_AS_STRING. + * + * An "unset" or #NULL signature is considered the same as an empty + * signature. In fact dbus_message_get_signature() will never return + * #NULL. + * + * @param message the message + * @param signature the type signature or #NULL to unset + * @returns #FALSE if no memory + */ +static dbus_bool_t +_dbus_message_set_signature (DBusMessage *message, + const char *signature) +{ + _dbus_return_val_if_fail (message != NULL, FALSE); + _dbus_return_val_if_fail (!message->locked, FALSE); + _dbus_return_val_if_fail (signature == NULL || + _dbus_check_is_valid_signature (signature)); + /* can't delete the signature if you have a message body */ + _dbus_return_val_if_fail (_dbus_string_get_length (&message->body) == 0 || + signature != NULL); + + return set_or_delete_string_field (message, + DBUS_HEADER_FIELD_SIGNATURE, + DBUS_TYPE_SIGNATURE, + signature); +} +#endif + +/* Message Cache + * + * We cache some DBusMessage to reduce the overhead of allocating + * them. In my profiling this consistently made about an 8% + * difference. It avoids the malloc for the message, the malloc for + * the slot list, the malloc for the header string and body string, + * and the associated free() calls. It does introduce another global + * lock which could be a performance issue in certain cases. + * + * For the echo client/server the round trip time goes from around + * .000077 to .000069 with the message cache on my laptop. The sysprof + * change is as follows (numbers are cumulative percentage): + * + * with message cache implemented as array as it is now (0.000069 per): + * new_empty_header 1.46 + * mutex_lock 0.56 # i.e. _DBUS_LOCK(message_cache) + * mutex_unlock 0.25 + * self 0.41 + * unref 2.24 + * self 0.68 + * list_clear 0.43 + * mutex_lock 0.33 # i.e. _DBUS_LOCK(message_cache) + * mutex_unlock 0.25 + * + * with message cache implemented as list (0.000070 per roundtrip): + * new_empty_header 2.72 + * list_pop_first 1.88 + * unref 3.3 + * list_prepend 1.63 + * + * without cache (0.000077 per roundtrip): + * new_empty_header 6.7 + * string_init_preallocated 3.43 + * dbus_malloc 2.43 + * dbus_malloc0 2.59 + * + * unref 4.02 + * string_free 1.82 + * dbus_free 1.63 + * dbus_free 0.71 + * + * If you implement the message_cache with a list, the primary reason + * it's slower is that you add another thread lock (on the DBusList + * mempool). + */ + +/** Avoid caching huge messages */ +#define MAX_MESSAGE_SIZE_TO_CACHE 10 * _DBUS_ONE_KILOBYTE + +/** Avoid caching too many messages */ +#define MAX_MESSAGE_CACHE_SIZE 5 + +_DBUS_DEFINE_GLOBAL_LOCK (message_cache); +static DBusMessage *message_cache[MAX_MESSAGE_CACHE_SIZE]; +static int message_cache_count = 0; +static dbus_bool_t message_cache_shutdown_registered = FALSE; + +static void +dbus_message_cache_shutdown (void *data) +{ + int i; + + _DBUS_LOCK (message_cache); + + i = 0; + while (i < MAX_MESSAGE_CACHE_SIZE) + { + if (message_cache[i]) + dbus_message_finalize (message_cache[i]); + + ++i; + } + + message_cache_count = 0; + message_cache_shutdown_registered = FALSE; + + _DBUS_UNLOCK (message_cache); +} + +/** + * Tries to get a message from the message cache. The retrieved + * message will have junk in it, so it still needs to be cleared out + * in dbus_message_new_empty_header() + * + * @returns the message, or #NULL if none cached + */ +static DBusMessage* +dbus_message_get_cached (void) +{ + DBusMessage *message; + int i; + + message = NULL; + + _DBUS_LOCK (message_cache); + + _dbus_assert (message_cache_count >= 0); + + if (message_cache_count == 0) + { + _DBUS_UNLOCK (message_cache); + return NULL; + } + + /* This is not necessarily true unless count > 0, and + * message_cache is uninitialized until the shutdown is + * registered + */ + _dbus_assert (message_cache_shutdown_registered); + + i = 0; + while (i < MAX_MESSAGE_CACHE_SIZE) + { + if (message_cache[i]) + { + message = message_cache[i]; + message_cache[i] = NULL; + message_cache_count -= 1; + break; + } + ++i; + } + _dbus_assert (message_cache_count >= 0); + _dbus_assert (i < MAX_MESSAGE_CACHE_SIZE); + _dbus_assert (message != NULL); + + _dbus_assert (message->refcount.value == 0); + _dbus_assert (message->size_counters == NULL); + + _DBUS_UNLOCK (message_cache); + + return message; +} + +static void +free_size_counter (void *element, + void *data) +{ + DBusCounter *counter = element; + DBusMessage *message = data; + + _dbus_counter_adjust (counter, - message->size_counter_delta); + + _dbus_counter_unref (counter); +} + +/** + * Tries to cache a message, otherwise finalize it. + * + * @param message the message + */ +static void +dbus_message_cache_or_finalize (DBusMessage *message) +{ + dbus_bool_t was_cached; + int i; + + _dbus_assert (message->refcount.value == 0); + + /* This calls application code and has to be done first thing + * without holding the lock + */ + _dbus_data_slot_list_clear (&message->slot_list); + + _dbus_list_foreach (&message->size_counters, + free_size_counter, message); + _dbus_list_clear (&message->size_counters); + + was_cached = FALSE; + + _DBUS_LOCK (message_cache); + + if (!message_cache_shutdown_registered) + { + _dbus_assert (message_cache_count == 0); + + if (!_dbus_register_shutdown_func (dbus_message_cache_shutdown, NULL)) + goto out; + + i = 0; + while (i < MAX_MESSAGE_CACHE_SIZE) + { + message_cache[i] = NULL; + ++i; + } + + message_cache_shutdown_registered = TRUE; + } + + _dbus_assert (message_cache_count >= 0); + + if ((_dbus_string_get_length (&message->header.data) + + _dbus_string_get_length (&message->body)) > + MAX_MESSAGE_SIZE_TO_CACHE) + goto out; + + if (message_cache_count >= MAX_MESSAGE_CACHE_SIZE) + goto out; + + /* Find empty slot */ + i = 0; + while (message_cache[i] != NULL) + ++i; + + _dbus_assert (i < MAX_MESSAGE_CACHE_SIZE); + + _dbus_assert (message_cache[i] == NULL); + message_cache[i] = message; + message_cache_count += 1; + was_cached = TRUE; +#ifndef DBUS_DISABLE_CHECKS + message->in_cache = TRUE; +#endif + + out: + _dbus_assert (message->refcount.value == 0); + + _DBUS_UNLOCK (message_cache); + + if (!was_cached) + dbus_message_finalize (message); +} + +#ifndef DBUS_DISABLE_CHECKS +static dbus_bool_t +_dbus_message_iter_check (DBusMessageRealIter *iter) +{ + if (iter == NULL) + { + _dbus_warn_check_failed ("dbus message iterator is NULL\n"); + return FALSE; + } + + if (iter->iter_type == DBUS_MESSAGE_ITER_TYPE_READER) + { + if (iter->u.reader.byte_order != iter->message->byte_order) + { + _dbus_warn_check_failed ("dbus message changed byte order since iterator was created\n"); + return FALSE; + } + /* because we swap the message into compiler order when you init an iter */ + _dbus_assert (iter->u.reader.byte_order == DBUS_COMPILER_BYTE_ORDER); + } + else if (iter->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER) + { + if (iter->u.writer.byte_order != iter->message->byte_order) + { + _dbus_warn_check_failed ("dbus message changed byte order since append iterator was created\n"); + return FALSE; + } + /* because we swap the message into compiler order when you init an iter */ + _dbus_assert (iter->u.writer.byte_order == DBUS_COMPILER_BYTE_ORDER); + } + else + { + _dbus_warn_check_failed ("dbus message iterator looks uninitialized or corrupted\n"); + return FALSE; + } + + if (iter->changed_stamp != iter->message->changed_stamp) + { + _dbus_warn_check_failed ("dbus message iterator invalid because the message has been modified (or perhaps the iterator is just uninitialized)\n"); + return FALSE; + } + + return TRUE; +} +#endif /* DBUS_DISABLE_CHECKS */ + +/** + * Implementation of the varargs arg-getting functions. + * dbus_message_get_args() is the place to go for complete + * documentation. + * + * @see dbus_message_get_args + * @param iter the message iter + * @param error error to be filled in + * @param first_arg_type type of the first argument + * @param var_args return location for first argument, followed by list of type/location pairs + * @returns #FALSE if error was set + */ +dbus_bool_t +_dbus_message_iter_get_args_valist (DBusMessageIter *iter, + DBusError *error, + int first_arg_type, + va_list var_args) +{ + DBusMessageRealIter *real = (DBusMessageRealIter *)iter; + int spec_type, msg_type, i; + dbus_bool_t retval; + + _dbus_assert (_dbus_message_iter_check (real)); + + retval = FALSE; + + spec_type = first_arg_type; + i = 0; + + while (spec_type != DBUS_TYPE_INVALID) + { + msg_type = dbus_message_iter_get_arg_type (iter); + + if (msg_type != spec_type) + { + dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, + "Argument %d is specified to be of type \"%s\", but " + "is actually of type \"%s\"\n", i, + _dbus_type_to_string (spec_type), + _dbus_type_to_string (msg_type)); + + goto out; + } + + if (dbus_type_is_basic (spec_type)) + { + DBusBasicValue *ptr; + + ptr = va_arg (var_args, DBusBasicValue*); + + _dbus_assert (ptr != NULL); + + _dbus_type_reader_read_basic (&real->u.reader, + ptr); + } + else if (spec_type == DBUS_TYPE_ARRAY) + { + int element_type; + int spec_element_type; + const DBusBasicValue **ptr; + int *n_elements_p; + DBusTypeReader array; + + spec_element_type = va_arg (var_args, int); + element_type = _dbus_type_reader_get_element_type (&real->u.reader); + + if (spec_element_type != element_type) + { + dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, + "Argument %d is specified to be an array of \"%s\", but " + "is actually an array of \"%s\"\n", + i, + _dbus_type_to_string (spec_element_type), + _dbus_type_to_string (element_type)); + + goto out; + } + + if (dbus_type_is_fixed (spec_element_type)) + { + ptr = va_arg (var_args, const DBusBasicValue**); + n_elements_p = va_arg (var_args, int*); + + _dbus_assert (ptr != NULL); + _dbus_assert (n_elements_p != NULL); + + _dbus_type_reader_recurse (&real->u.reader, &array); + + _dbus_type_reader_read_fixed_multi (&array, + ptr, n_elements_p); + } + else if (spec_element_type == DBUS_TYPE_STRING || + spec_element_type == DBUS_TYPE_SIGNATURE || + spec_element_type == DBUS_TYPE_OBJECT_PATH) + { + char ***str_array_p; + int n_elements; + char **str_array; + + str_array_p = va_arg (var_args, char***); + n_elements_p = va_arg (var_args, int*); + + _dbus_assert (str_array_p != NULL); + _dbus_assert (n_elements_p != NULL); + + /* Count elements in the array */ + _dbus_type_reader_recurse (&real->u.reader, &array); + + n_elements = 0; + while (_dbus_type_reader_get_current_type (&array) != DBUS_TYPE_INVALID) + { + ++n_elements; + _dbus_type_reader_next (&array); + } + + str_array = dbus_new0 (char*, n_elements + 1); + if (str_array == NULL) + { + _DBUS_SET_OOM (error); + goto out; + } + + /* Now go through and dup each string */ + _dbus_type_reader_recurse (&real->u.reader, &array); + + i = 0; + while (i < n_elements) + { + const char *s; + _dbus_type_reader_read_basic (&array, + &s); + + str_array[i] = _dbus_strdup (s); + if (str_array[i] == NULL) + { + dbus_free_string_array (str_array); + _DBUS_SET_OOM (error); + goto out; + } + + ++i; + + if (!_dbus_type_reader_next (&array)) + _dbus_assert (i == n_elements); + } + + _dbus_assert (_dbus_type_reader_get_current_type (&array) == DBUS_TYPE_INVALID); + _dbus_assert (i == n_elements); + _dbus_assert (str_array[i] == NULL); + + *str_array_p = str_array; + *n_elements_p = n_elements; + } +#ifndef DBUS_DISABLE_CHECKS + else + { + _dbus_warn ("you can't read arrays of container types (struct, variant, array) with %s for now\n", + _DBUS_FUNCTION_NAME); + goto out; + } +#endif + } +#ifndef DBUS_DISABLE_CHECKS + else + { + _dbus_warn ("you can only read arrays and basic types with %s for now\n", + _DBUS_FUNCTION_NAME); + goto out; + } +#endif + + spec_type = va_arg (var_args, int); + if (!_dbus_type_reader_next (&real->u.reader) && spec_type != DBUS_TYPE_INVALID) + { + dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, + "Message has only %d arguments, but more were expected", i); + goto out; + } + + i++; + } + + retval = TRUE; + + out: + + return retval; +} + +/** @} */ + +/** + * @defgroup DBusMessage DBusMessage + * @ingroup DBus + * @brief Message to be sent or received over a #DBusConnection. + * + * A DBusMessage is the most basic unit of communication over a + * DBusConnection. A DBusConnection represents a stream of messages + * received from a remote application, and a stream of messages + * sent to a remote application. + * + * A message has a message type, returned from + * dbus_message_get_type(). This indicates whether the message is a + * method call, a reply to a method call, a signal, or an error reply. + * + * A message has header fields such as the sender, destination, method + * or signal name, and so forth. DBusMessage has accessor functions for + * these, such as dbus_message_get_member(). + * + * Convenience functions dbus_message_is_method_call(), dbus_message_is_signal(), + * and dbus_message_is_error() check several header fields at once and are + * slightly more efficient than checking the header fields with individual + * accessor functions. + * + * Finally, a message has arguments. The number and types of arguments + * are in the message's signature header field (accessed with + * dbus_message_get_signature()). Simple argument values are usually + * retrieved with dbus_message_get_args() but more complex values such + * as structs may require the use of #DBusMessageIter. + * + * The D-Bus specification goes into some more detail about header fields and + * message types. + * + * @{ + */ + +/** + * @typedef DBusMessage + * + * Opaque data type representing a message received from or to be + * sent to another application. + */ + +/** + * Returns the serial of a message or 0 if none has been specified. + * The message's serial number is provided by the application sending + * the message and is used to identify replies to this message. + * + * All messages received on a connection will have a serial provided + * by the remote application. + * + * For messages you're sending, dbus_connection_send() will assign a + * serial and return it to you. + * + * @param message the message + * @returns the serial + */ +dbus_uint32_t +dbus_message_get_serial (DBusMessage *message) +{ + _dbus_return_val_if_fail (message != NULL, 0); + + return _dbus_header_get_serial (&message->header); +} + +/** + * Sets the reply serial of a message (the serial of the message this + * is a reply to). + * + * @param message the message + * @param reply_serial the serial we're replying to + * @returns #FALSE if not enough memory + */ +dbus_bool_t +dbus_message_set_reply_serial (DBusMessage *message, + dbus_uint32_t reply_serial) +{ + _dbus_return_val_if_fail (message != NULL, FALSE); + _dbus_return_val_if_fail (!message->locked, FALSE); + _dbus_return_val_if_fail (reply_serial != 0, FALSE); /* 0 is invalid */ + + return _dbus_header_set_field_basic (&message->header, + DBUS_HEADER_FIELD_REPLY_SERIAL, + DBUS_TYPE_UINT32, + &reply_serial); +} + +/** + * Returns the serial that the message is a reply to or 0 if none. + * + * @param message the message + * @returns the reply serial + */ +dbus_uint32_t +dbus_message_get_reply_serial (DBusMessage *message) +{ + dbus_uint32_t v_UINT32; + + _dbus_return_val_if_fail (message != NULL, 0); + + if (_dbus_header_get_field_basic (&message->header, + DBUS_HEADER_FIELD_REPLY_SERIAL, + DBUS_TYPE_UINT32, + &v_UINT32)) + return v_UINT32; + else + return 0; +} + +static void +dbus_message_finalize (DBusMessage *message) +{ + _dbus_assert (message->refcount.value == 0); + + /* This calls application callbacks! */ + _dbus_data_slot_list_free (&message->slot_list); + + _dbus_list_foreach (&message->size_counters, + free_size_counter, message); + _dbus_list_clear (&message->size_counters); + + _dbus_header_free (&message->header); + _dbus_string_free (&message->body); + + _dbus_assert (message->refcount.value == 0); + + dbus_free (message); +} + +static DBusMessage* +dbus_message_new_empty_header (void) +{ + DBusMessage *message; + dbus_bool_t from_cache; + + message = dbus_message_get_cached (); + + if (message != NULL) + { + from_cache = TRUE; + } + else + { + from_cache = FALSE; + message = dbus_new (DBusMessage, 1); + if (message == NULL) + return NULL; +#ifndef DBUS_DISABLE_CHECKS + message->generation = _dbus_current_generation; +#endif + } + + message->refcount.value = 1; + message->byte_order = DBUS_COMPILER_BYTE_ORDER; + message->locked = FALSE; +#ifndef DBUS_DISABLE_CHECKS + message->in_cache = FALSE; +#endif + message->size_counters = NULL; + message->size_counter_delta = 0; + message->changed_stamp = 0; + + if (!from_cache) + _dbus_data_slot_list_init (&message->slot_list); + + if (from_cache) + { + _dbus_header_reinit (&message->header, message->byte_order); + _dbus_string_set_length (&message->body, 0); + } + else + { + if (!_dbus_header_init (&message->header, message->byte_order)) + { + dbus_free (message); + return NULL; + } + + if (!_dbus_string_init_preallocated (&message->body, 32)) + { + _dbus_header_free (&message->header); + dbus_free (message); + return NULL; + } + } + + return message; +} + +/** + * Constructs a new message of the given message type. + * Types include #DBUS_MESSAGE_TYPE_METHOD_CALL, + * #DBUS_MESSAGE_TYPE_SIGNAL, and so forth. + * + * Usually you want to use dbus_message_new_method_call(), + * dbus_message_new_method_return(), dbus_message_new_signal(), + * or dbus_message_new_error() instead. + * + * @param message_type type of message + * @returns new message or #NULL if no memory + */ +DBusMessage* +dbus_message_new (int message_type) +{ + DBusMessage *message; + + _dbus_return_val_if_fail (message_type != DBUS_MESSAGE_TYPE_INVALID, NULL); + + message = dbus_message_new_empty_header (); + if (message == NULL) + return NULL; + + if (!_dbus_header_create (&message->header, + message_type, + NULL, NULL, NULL, NULL, NULL)) + { + dbus_message_unref (message); + return NULL; + } + + return message; +} + +/** + * Constructs a new message to invoke a method on a remote + * object. Returns #NULL if memory can't be allocated for the + * message. The destination may be #NULL in which case no destination + * is set; this is appropriate when using D-Bus in a peer-to-peer + * context (no message bus). The interface may be #NULL, which means + * that if multiple methods with the given name exist it is undefined + * which one will be invoked. + * + * The path and method names may not be #NULL. + * + * Destination, path, interface, and method name can't contain + * any invalid characters (see the D-Bus specification). + * + * @param destination name that the message should be sent to or #NULL + * @param path object path the message should be sent to + * @param interface interface to invoke method on, or #NULL + * @param method method to invoke + * + * @returns a new DBusMessage, free with dbus_message_unref() + */ +DBusMessage* +dbus_message_new_method_call (const char *destination, + const char *path, + const char *interface, + const char *method) +{ + DBusMessage *message; + + _dbus_return_val_if_fail (path != NULL, NULL); + _dbus_return_val_if_fail (method != NULL, NULL); + _dbus_return_val_if_fail (destination == NULL || + _dbus_check_is_valid_bus_name (destination), NULL); + _dbus_return_val_if_fail (_dbus_check_is_valid_path (path), NULL); + _dbus_return_val_if_fail (interface == NULL || + _dbus_check_is_valid_interface (interface), NULL); + _dbus_return_val_if_fail (_dbus_check_is_valid_member (method), NULL); + + message = dbus_message_new_empty_header (); + if (message == NULL) + return NULL; + + if (!_dbus_header_create (&message->header, + DBUS_MESSAGE_TYPE_METHOD_CALL, + destination, path, interface, method, NULL)) + { + dbus_message_unref (message); + return NULL; + } + + return message; +} + +/** + * Constructs a message that is a reply to a method call. Returns + * #NULL if memory can't be allocated for the message. + * + * @param method_call the message being replied to + * @returns a new DBusMessage, free with dbus_message_unref() + */ +DBusMessage* +dbus_message_new_method_return (DBusMessage *method_call) +{ + DBusMessage *message; + const char *sender; + + _dbus_return_val_if_fail (method_call != NULL, NULL); + + sender = dbus_message_get_sender (method_call); + + /* sender is allowed to be null here in peer-to-peer case */ + + message = dbus_message_new_empty_header (); + if (message == NULL) + return NULL; + + if (!_dbus_header_create (&message->header, + DBUS_MESSAGE_TYPE_METHOD_RETURN, + sender, NULL, NULL, NULL, NULL)) + { + dbus_message_unref (message); + return NULL; + } + + dbus_message_set_no_reply (message, TRUE); + + if (!dbus_message_set_reply_serial (message, + dbus_message_get_serial (method_call))) + { + dbus_message_unref (message); + return NULL; + } + + return message; +} + +/** + * Constructs a new message representing a signal emission. Returns + * #NULL if memory can't be allocated for the message. A signal is + * identified by its originating object path, interface, and the name + * of the signal. + * + * Path, interface, and signal name must all be valid (the D-Bus + * specification defines the syntax of these fields). + * + * @param path the path to the object emitting the signal + * @param interface the interface the signal is emitted from + * @param name name of the signal + * @returns a new DBusMessage, free with dbus_message_unref() + */ +DBusMessage* +dbus_message_new_signal (const char *path, + const char *interface, + const char *name) +{ + DBusMessage *message; + + _dbus_return_val_if_fail (path != NULL, NULL); + _dbus_return_val_if_fail (interface != NULL, NULL); + _dbus_return_val_if_fail (name != NULL, NULL); + _dbus_return_val_if_fail (_dbus_check_is_valid_path (path), NULL); + _dbus_return_val_if_fail (_dbus_check_is_valid_interface (interface), NULL); + _dbus_return_val_if_fail (_dbus_check_is_valid_member (name), NULL); + + message = dbus_message_new_empty_header (); + if (message == NULL) + return NULL; + + if (!_dbus_header_create (&message->header, + DBUS_MESSAGE_TYPE_SIGNAL, + NULL, path, interface, name, NULL)) + { + dbus_message_unref (message); + return NULL; + } + + dbus_message_set_no_reply (message, TRUE); + + return message; +} + +/** + * Creates a new message that is an error reply to another message. + * Error replies are most common in response to method calls, but + * can be returned in reply to any message. + * + * The error name must be a valid error name according to the syntax + * given in the D-Bus specification. If you don't want to make + * up an error name just use #DBUS_ERROR_FAILED. + * + * @param reply_to the message we're replying to + * @param error_name the error name + * @param error_message the error message string (or #NULL for none, but please give a message) + * @returns a new error message object, free with dbus_message_unref() + */ +DBusMessage* +dbus_message_new_error (DBusMessage *reply_to, + const char *error_name, + const char *error_message) +{ + DBusMessage *message; + const char *sender; + DBusMessageIter iter; + + _dbus_return_val_if_fail (reply_to != NULL, NULL); + _dbus_return_val_if_fail (error_name != NULL, NULL); + _dbus_return_val_if_fail (_dbus_check_is_valid_error_name (error_name), NULL); + + sender = dbus_message_get_sender (reply_to); + + /* sender may be NULL for non-message-bus case or + * when the message bus is dealing with an unregistered + * connection. + */ + message = dbus_message_new_empty_header (); + if (message == NULL) + return NULL; + + if (!_dbus_header_create (&message->header, + DBUS_MESSAGE_TYPE_ERROR, + sender, NULL, NULL, NULL, error_name)) + { + dbus_message_unref (message); + return NULL; + } + + dbus_message_set_no_reply (message, TRUE); + + if (!dbus_message_set_reply_serial (message, + dbus_message_get_serial (reply_to))) + { + dbus_message_unref (message); + return NULL; + } + + if (error_message != NULL) + { + dbus_message_iter_init_append (message, &iter); + if (!dbus_message_iter_append_basic (&iter, + DBUS_TYPE_STRING, + &error_message)) + { + dbus_message_unref (message); + return NULL; + } + } + + return message; +} + +/** + * Creates a new message that is an error reply to another message, allowing + * you to use printf formatting. + * + * See dbus_message_new_error() for details - this function is the same + * aside from the printf formatting. + * + * @todo add _DBUS_GNUC_PRINTF to this (requires moving _DBUS_GNUC_PRINTF to + * public header, see DBUS_DEPRECATED for an example) + * + * @param reply_to the original message + * @param error_name the error name + * @param error_format the error message format as with printf + * @param ... format string arguments + * @returns a new error message + */ +DBusMessage* +dbus_message_new_error_printf (DBusMessage *reply_to, + const char *error_name, + const char *error_format, + ...) +{ + va_list args; + DBusString str; + DBusMessage *message; + + _dbus_return_val_if_fail (reply_to != NULL, NULL); + _dbus_return_val_if_fail (error_name != NULL, NULL); + _dbus_return_val_if_fail (_dbus_check_is_valid_error_name (error_name), NULL); + + if (!_dbus_string_init (&str)) + return NULL; + + va_start (args, error_format); + + if (_dbus_string_append_printf_valist (&str, error_format, args)) + message = dbus_message_new_error (reply_to, error_name, + _dbus_string_get_const_data (&str)); + else + message = NULL; + + _dbus_string_free (&str); + + va_end (args); + + return message; +} + + +/** + * Creates a new message that is an exact replica of the message + * specified, except that its refcount is set to 1, its message serial + * is reset to 0, and if the original message was "locked" (in the + * outgoing message queue and thus not modifiable) the new message + * will not be locked. + * + * @param message the message + * @returns the new message.or #NULL if not enough memory + */ +DBusMessage * +dbus_message_copy (const DBusMessage *message) +{ + DBusMessage *retval; + + _dbus_return_val_if_fail (message != NULL, NULL); + + retval = dbus_new0 (DBusMessage, 1); + if (retval == NULL) + return NULL; + + retval->refcount.value = 1; + retval->byte_order = message->byte_order; + retval->locked = FALSE; +#ifndef DBUS_DISABLE_CHECKS + retval->generation = message->generation; +#endif + + if (!_dbus_header_copy (&message->header, &retval->header)) + { + dbus_free (retval); + return NULL; + } + + if (!_dbus_string_init_preallocated (&retval->body, + _dbus_string_get_length (&message->body))) + { + _dbus_header_free (&retval->header); + dbus_free (retval); + return NULL; + } + + if (!_dbus_string_copy (&message->body, 0, + &retval->body, 0)) + goto failed_copy; + + return retval; + + failed_copy: + _dbus_header_free (&retval->header); + _dbus_string_free (&retval->body); + dbus_free (retval); + + return NULL; +} + + +/** + * Increments the reference count of a DBusMessage. + * + * @param message the message + * @returns the message + * @see dbus_message_unref + */ +DBusMessage * +dbus_message_ref (DBusMessage *message) +{ + dbus_int32_t old_refcount; + + _dbus_return_val_if_fail (message != NULL, NULL); + _dbus_return_val_if_fail (message->generation == _dbus_current_generation, NULL); + _dbus_return_val_if_fail (!message->in_cache, NULL); + + old_refcount = _dbus_atomic_inc (&message->refcount); + _dbus_assert (old_refcount >= 1); + + return message; +} + +/** + * Decrements the reference count of a DBusMessage, freeing the + * message if the count reaches 0. + * + * @param message the message + * @see dbus_message_ref + */ +void +dbus_message_unref (DBusMessage *message) +{ + dbus_int32_t old_refcount; + + _dbus_return_if_fail (message != NULL); + _dbus_return_if_fail (message->generation == _dbus_current_generation); + _dbus_return_if_fail (!message->in_cache); + + old_refcount = _dbus_atomic_dec (&message->refcount); + + _dbus_assert (old_refcount >= 0); + + if (old_refcount == 1) + { + /* Calls application callbacks! */ + dbus_message_cache_or_finalize (message); + } +} + +/** + * Gets the type of a message. Types include + * #DBUS_MESSAGE_TYPE_METHOD_CALL, #DBUS_MESSAGE_TYPE_METHOD_RETURN, + * #DBUS_MESSAGE_TYPE_ERROR, #DBUS_MESSAGE_TYPE_SIGNAL, but other + * types are allowed and all code must silently ignore messages of + * unknown type. #DBUS_MESSAGE_TYPE_INVALID will never be returned. + * + * @param message the message + * @returns the type of the message + */ +int +dbus_message_get_type (DBusMessage *message) +{ + _dbus_return_val_if_fail (message != NULL, DBUS_MESSAGE_TYPE_INVALID); + + return _dbus_header_get_message_type (&message->header); +} + +/** + * Appends fields to a message given a variable argument list. The + * variable argument list should contain the type of each argument + * followed by the value to append. Appendable types are basic types, + * and arrays of fixed-length basic types. To append variable-length + * basic types, or any more complex value, you have to use an iterator + * rather than this function. + * + * To append a basic type, specify its type code followed by the + * address of the value. For example: + * + * @code + * + * dbus_int32_t v_INT32 = 42; + * const char *v_STRING = "Hello World"; + * dbus_message_append_args (message, + * DBUS_TYPE_INT32, &v_INT32, + * DBUS_TYPE_STRING, &v_STRING, + * DBUS_TYPE_INVALID); + * @endcode + * + * To append an array of fixed-length basic types, pass in the + * DBUS_TYPE_ARRAY typecode, the element typecode, the address of + * the array pointer, and a 32-bit integer giving the number of + * elements in the array. So for example: + * @code + * const dbus_int32_t array[] = { 1, 2, 3 }; + * const dbus_int32_t *v_ARRAY = array; + * dbus_message_append_args (message, + * DBUS_TYPE_ARRAY, DBUS_TYPE_INT32, &v_ARRAY, 3, + * DBUS_TYPE_INVALID); + * @endcode + * + * @warning in C, given "int array[]", "&array == array" (the + * comp.lang.c FAQ says otherwise, but gcc and the FAQ don't agree). + * So if you're using an array instead of a pointer you have to create + * a pointer variable, assign the array to it, then take the address + * of the pointer variable. For strings it works to write + * const char *array = "Hello" and then use &array though. + * + * The last argument to this function must be #DBUS_TYPE_INVALID, + * marking the end of the argument list. If you don't do this + * then libdbus won't know to stop and will read invalid memory. + * + * String/signature/path arrays should be passed in as "const char*** + * address_of_array" and "int n_elements" + * + * @todo support DBUS_TYPE_STRUCT and DBUS_TYPE_VARIANT and complex arrays + * + * @todo If this fails due to lack of memory, the message is hosed and + * you have to start over building the whole message. + * + * @param message the message + * @param first_arg_type type of the first argument + * @param ... value of first argument, list of additional type-value pairs + * @returns #TRUE on success + */ +dbus_bool_t +dbus_message_append_args (DBusMessage *message, + int first_arg_type, + ...) +{ + dbus_bool_t retval; + va_list var_args; + + _dbus_return_val_if_fail (message != NULL, FALSE); + + va_start (var_args, first_arg_type); + retval = dbus_message_append_args_valist (message, + first_arg_type, + var_args); + va_end (var_args); + + return retval; +} + +/** + * Like dbus_message_append_args() but takes a va_list for use by language bindings. + * + * @todo for now, if this function fails due to OOM it will leave + * the message half-written and you have to discard the message + * and start over. + * + * @see dbus_message_append_args. + * @param message the message + * @param first_arg_type type of first argument + * @param var_args value of first argument, then list of type/value pairs + * @returns #TRUE on success + */ +dbus_bool_t +dbus_message_append_args_valist (DBusMessage *message, + int first_arg_type, + va_list var_args) +{ + int type; + DBusMessageIter iter; + + _dbus_return_val_if_fail (message != NULL, FALSE); + + type = first_arg_type; + + dbus_message_iter_init_append (message, &iter); + + while (type != DBUS_TYPE_INVALID) + { + if (dbus_type_is_basic (type)) + { + const DBusBasicValue *value; + value = va_arg (var_args, const DBusBasicValue*); + + if (!dbus_message_iter_append_basic (&iter, + type, + value)) + goto failed; + } + else if (type == DBUS_TYPE_ARRAY) + { + int element_type; + DBusMessageIter array; + char buf[2]; + + element_type = va_arg (var_args, int); + + buf[0] = element_type; + buf[1] = '\0'; + if (!dbus_message_iter_open_container (&iter, + DBUS_TYPE_ARRAY, + buf, + &array)) + goto failed; + + if (dbus_type_is_fixed (element_type)) + { + const DBusBasicValue **value; + int n_elements; + + value = va_arg (var_args, const DBusBasicValue**); + n_elements = va_arg (var_args, int); + + if (!dbus_message_iter_append_fixed_array (&array, + element_type, + value, + n_elements)) + goto failed; + } + else if (element_type == DBUS_TYPE_STRING || + element_type == DBUS_TYPE_SIGNATURE || + element_type == DBUS_TYPE_OBJECT_PATH) + { + const char ***value_p; + const char **value; + int n_elements; + int i; + + value_p = va_arg (var_args, const char***); + n_elements = va_arg (var_args, int); + + value = *value_p; + + i = 0; + while (i < n_elements) + { + if (!dbus_message_iter_append_basic (&array, + element_type, + &value[i])) + goto failed; + ++i; + } + } + else + { + _dbus_warn ("arrays of %s can't be appended with %s for now\n", + _dbus_type_to_string (element_type), + _DBUS_FUNCTION_NAME); + goto failed; + } + + if (!dbus_message_iter_close_container (&iter, &array)) + goto failed; + } +#ifndef DBUS_DISABLE_CHECKS + else + { + _dbus_warn ("type %s isn't supported yet in %s\n", + _dbus_type_to_string (type), _DBUS_FUNCTION_NAME); + goto failed; + } +#endif + + type = va_arg (var_args, int); + } + + return TRUE; + + failed: + return FALSE; +} + +/** + * Gets arguments from a message given a variable argument list. The + * supported types include those supported by + * dbus_message_append_args(); that is, basic types and arrays of + * fixed-length basic types. The arguments are the same as they would + * be for dbus_message_iter_get_basic() or + * dbus_message_iter_get_fixed_array(). + * + * In addition to those types, arrays of string, object path, and + * signature are supported; but these are returned as allocated memory + * and must be freed with dbus_free_string_array(), while the other + * types are returned as const references. To get a string array + * pass in "char ***array_location" and "int *n_elements" + * + * The variable argument list should contain the type of the argument + * followed by a pointer to where the value should be stored. The list + * is terminated with #DBUS_TYPE_INVALID. + * + * Except for string arrays, the returned values are constant; do not + * free them. They point into the #DBusMessage. + * + * If the requested arguments are not present, or do not have the + * requested types, then an error will be set. + * + * If more arguments than requested are present, the requested + * arguments are returned and the extra arguments are ignored. + * + * @todo support DBUS_TYPE_STRUCT and DBUS_TYPE_VARIANT and complex arrays + * + * @param message the message + * @param error error to be filled in on failure + * @param first_arg_type the first argument type + * @param ... location for first argument value, then list of type-location pairs + * @returns #FALSE if the error was set + */ +dbus_bool_t +dbus_message_get_args (DBusMessage *message, + DBusError *error, + int first_arg_type, + ...) +{ + dbus_bool_t retval; + va_list var_args; + + _dbus_return_val_if_fail (message != NULL, FALSE); + _dbus_return_val_if_error_is_set (error, FALSE); + + va_start (var_args, first_arg_type); + retval = dbus_message_get_args_valist (message, error, first_arg_type, var_args); + va_end (var_args); + + return retval; +} + +/** + * Like dbus_message_get_args but takes a va_list for use by language bindings. + * + * @see dbus_message_get_args + * @param message the message + * @param error error to be filled in + * @param first_arg_type type of the first argument + * @param var_args return location for first argument, followed by list of type/location pairs + * @returns #FALSE if error was set + */ +dbus_bool_t +dbus_message_get_args_valist (DBusMessage *message, + DBusError *error, + int first_arg_type, + va_list var_args) +{ + DBusMessageIter iter; + + _dbus_return_val_if_fail (message != NULL, FALSE); + _dbus_return_val_if_error_is_set (error, FALSE); + + dbus_message_iter_init (message, &iter); + return _dbus_message_iter_get_args_valist (&iter, error, first_arg_type, var_args); +} + +static void +_dbus_message_iter_init_common (DBusMessage *message, + DBusMessageRealIter *real, + int iter_type) +{ + _dbus_assert (sizeof (DBusMessageRealIter) <= sizeof (DBusMessageIter)); + + /* Since the iterator will read or write who-knows-what from the + * message, we need to get in the right byte order + */ + ensure_byte_order (message); + + real->message = message; + real->changed_stamp = message->changed_stamp; + real->iter_type = iter_type; + real->sig_refcount = 0; +} + +/** + * Initializes a #DBusMessageIter for reading the arguments of the + * message passed in. + * + * When possible, dbus_message_get_args() is much more convenient. + * Some types of argument can only be read with #DBusMessageIter + * however. + * + * The easiest way to iterate is like this: + * @code + * dbus_message_iter_init (message, &iter); + * while ((current_type = dbus_message_iter_get_arg_type (&iter)) != DBUS_TYPE_INVALID) + * dbus_message_iter_next (&iter); + * @endcode + * + * #DBusMessageIter contains no allocated memory; it need not be + * freed, and can be copied by assignment or memcpy(). + * + * @param message the message + * @param iter pointer to an iterator to initialize + * @returns #FALSE if the message has no arguments + */ +dbus_bool_t +dbus_message_iter_init (DBusMessage *message, + DBusMessageIter *iter) +{ + DBusMessageRealIter *real = (DBusMessageRealIter *)iter; + const DBusString *type_str; + int type_pos; + + _dbus_return_val_if_fail (message != NULL, FALSE); + _dbus_return_val_if_fail (iter != NULL, FALSE); + + get_const_signature (&message->header, &type_str, &type_pos); + + _dbus_message_iter_init_common (message, real, + DBUS_MESSAGE_ITER_TYPE_READER); + + _dbus_type_reader_init (&real->u.reader, + message->byte_order, + type_str, type_pos, + &message->body, + 0); + + return _dbus_type_reader_get_current_type (&real->u.reader) != DBUS_TYPE_INVALID; +} + +/** + * Checks if an iterator has any more fields. + * + * @param iter the message iter + * @returns #TRUE if there are more fields following + */ +dbus_bool_t +dbus_message_iter_has_next (DBusMessageIter *iter) +{ + DBusMessageRealIter *real = (DBusMessageRealIter *)iter; + + _dbus_return_val_if_fail (_dbus_message_iter_check (real), FALSE); + _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_READER, FALSE); + + return _dbus_type_reader_has_next (&real->u.reader); +} + +/** + * Moves the iterator to the next field, if any. If there's no next + * field, returns #FALSE. If the iterator moves forward, returns + * #TRUE. + * + * @param iter the message iter + * @returns #TRUE if the iterator was moved to the next field + */ +dbus_bool_t +dbus_message_iter_next (DBusMessageIter *iter) +{ + DBusMessageRealIter *real = (DBusMessageRealIter *)iter; + + _dbus_return_val_if_fail (_dbus_message_iter_check (real), FALSE); + _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_READER, FALSE); + + return _dbus_type_reader_next (&real->u.reader); +} + +/** + * Returns the argument type of the argument that the message iterator + * points to. If the iterator is at the end of the message, returns + * #DBUS_TYPE_INVALID. You can thus write a loop as follows: + * + * @code + * dbus_message_iter_init (&iter); + * while ((current_type = dbus_message_iter_get_arg_type (&iter)) != DBUS_TYPE_INVALID) + * dbus_message_iter_next (&iter); + * @endcode + * + * @param iter the message iter + * @returns the argument type + */ +int +dbus_message_iter_get_arg_type (DBusMessageIter *iter) +{ + DBusMessageRealIter *real = (DBusMessageRealIter *)iter; + + _dbus_return_val_if_fail (_dbus_message_iter_check (real), DBUS_TYPE_INVALID); + _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_READER, FALSE); + + return _dbus_type_reader_get_current_type (&real->u.reader); +} + +/** + * Returns the element type of the array that the message iterator + * points to. Note that you need to check that the iterator points to + * an array prior to using this function. + * + * @param iter the message iter + * @returns the array element type + */ +int +dbus_message_iter_get_element_type (DBusMessageIter *iter) +{ + DBusMessageRealIter *real = (DBusMessageRealIter *)iter; + + _dbus_return_val_if_fail (_dbus_message_iter_check (real), DBUS_TYPE_INVALID); + _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_READER, DBUS_TYPE_INVALID); + _dbus_return_val_if_fail (dbus_message_iter_get_arg_type (iter) == DBUS_TYPE_ARRAY, DBUS_TYPE_INVALID); + + return _dbus_type_reader_get_element_type (&real->u.reader); +} + +/** + * Recurses into a container value when reading values from a message, + * initializing a sub-iterator to use for traversing the child values + * of the container. + * + * Note that this recurses into a value, not a type, so you can only + * recurse if the value exists. The main implication of this is that + * if you have for example an empty array of array of int32, you can + * recurse into the outermost array, but it will have no values, so + * you won't be able to recurse further. There's no array of int32 to + * recurse into. + * + * If a container is an array of fixed-length types, it is much more + * efficient to use dbus_message_iter_get_fixed_array() to get the + * whole array in one shot, rather than individually walking over the + * array elements. + * + * Be sure you have somehow checked that + * dbus_message_iter_get_arg_type() matches the type you are expecting + * to recurse into. Results of this function are undefined if there is + * no container to recurse into at the current iterator position. + * + * @param iter the message iterator + * @param sub the sub-iterator to initialize + */ +void +dbus_message_iter_recurse (DBusMessageIter *iter, + DBusMessageIter *sub) +{ + DBusMessageRealIter *real = (DBusMessageRealIter *)iter; + DBusMessageRealIter *real_sub = (DBusMessageRealIter *)sub; + + _dbus_return_if_fail (_dbus_message_iter_check (real)); + _dbus_return_if_fail (sub != NULL); + + *real_sub = *real; + _dbus_type_reader_recurse (&real->u.reader, &real_sub->u.reader); +} + +/** + * Returns the current signature of a message iterator. This + * is useful primarily for dealing with variants; one can + * recurse into a variant and determine the signature of + * the variant's value. + * + * The returned string must be freed with dbus_free(). + * + * @param iter the message iterator + * @returns the contained signature, or NULL if out of memory + */ +char * +dbus_message_iter_get_signature (DBusMessageIter *iter) +{ + const DBusString *sig; + DBusString retstr; + char *ret; + int start, len; + DBusMessageRealIter *real = (DBusMessageRealIter *)iter; + + _dbus_return_val_if_fail (_dbus_message_iter_check (real), NULL); + + if (!_dbus_string_init (&retstr)) + return NULL; + + _dbus_type_reader_get_signature (&real->u.reader, &sig, + &start, &len); + if (!_dbus_string_append_len (&retstr, + _dbus_string_get_const_data (sig) + start, + len)) + return NULL; + if (!_dbus_string_steal_data (&retstr, &ret)) + return NULL; + _dbus_string_free (&retstr); + return ret; +} + +/** + * Reads a basic-typed value from the message iterator. + * Basic types are the non-containers such as integer and string. + * + * The value argument should be the address of a location to store + * the returned value. So for int32 it should be a "dbus_int32_t*" + * and for string a "const char**". The returned value is + * by reference and should not be freed. + * + * Be sure you have somehow checked that + * dbus_message_iter_get_arg_type() matches the type you are + * expecting, or you'll crash when you try to use an integer as a + * string or something. + * + * To read any container type (array, struct, dict) you will need + * to recurse into the container with dbus_message_iter_recurse(). + * If the container is an array of fixed-length values, you can + * get all the array elements at once with + * dbus_message_iter_get_fixed_array(). Otherwise, you have to + * iterate over the container's contents one value at a time. + * + * All basic-typed values are guaranteed to fit in 8 bytes. So you can + * write code like this: + * + * @code + * dbus_uint64_t value; + * int type; + * dbus_message_iter_get_basic (&read_iter, &value); + * type = dbus_message_iter_get_arg_type (&read_iter); + * dbus_message_iter_append_basic (&write_iter, type, &value); + * @endcode + * + * On some really obscure platforms dbus_uint64_t might not exist, if + * you need to worry about this you will know. dbus_uint64_t is just + * one example of a type that's large enough to hold any possible + * value, you could use a struct or char[8] instead if you like. + * + * @param iter the iterator + * @param value location to store the value + */ +void +dbus_message_iter_get_basic (DBusMessageIter *iter, + void *value) +{ + DBusMessageRealIter *real = (DBusMessageRealIter *)iter; + + _dbus_return_if_fail (_dbus_message_iter_check (real)); + _dbus_return_if_fail (value != NULL); + + _dbus_type_reader_read_basic (&real->u.reader, + value); +} + +/** + * Returns the number of bytes in the array as marshaled in the wire + * protocol. The iterator must currently be inside an array-typed + * value. + * + * This function is deprecated on the grounds that it is stupid. Why + * would you want to know how many bytes are in the array as marshaled + * in the wire protocol? For now, use the n_elements returned from + * dbus_message_iter_get_fixed_array() instead, or iterate over the + * array values and count them. + * + * @todo introduce a variant of this get_n_elements that returns + * the number of elements, though with a non-fixed array it will not + * be very efficient, so maybe it's not good. + * + * @param iter the iterator + * @returns the number of bytes in the array + */ +int +dbus_message_iter_get_array_len (DBusMessageIter *iter) +{ + DBusMessageRealIter *real = (DBusMessageRealIter *)iter; + + _dbus_return_val_if_fail (_dbus_message_iter_check (real), 0); + + return _dbus_type_reader_get_array_length (&real->u.reader); +} + +/** + * Reads a block of fixed-length values from the message iterator. + * Fixed-length values are those basic types that are not string-like, + * such as integers, bool, double. The returned block will be from the + * current position in the array until the end of the array. + * + * The message iter should be "in" the array (that is, you recurse into the + * array, and then you call dbus_message_iter_get_fixed_array() on the + * "sub-iterator" created by dbus_message_iter_recurse()). + * + * The value argument should be the address of a location to store the + * returned array. So for int32 it should be a "const dbus_int32_t**" + * The returned value is by reference and should not be freed. + * + * This function should only be used if dbus_type_is_fixed() returns + * #TRUE for the element type. + * + * If an array's elements are not fixed in size, you have to recurse + * into the array with dbus_message_iter_recurse() and read the + * elements one by one. + * + * Because the array is not copied, this function runs in constant + * time and is fast; it's much preferred over walking the entire array + * with an iterator. (However, you can always use + * dbus_message_iter_recurse(), even for fixed-length types; + * dbus_message_iter_get_fixed_array() is just an optimization.) + * + * @param iter the iterator + * @param value location to store the block + * @param n_elements number of elements in the block + */ +void +dbus_message_iter_get_fixed_array (DBusMessageIter *iter, + void *value, + int *n_elements) +{ + DBusMessageRealIter *real = (DBusMessageRealIter *)iter; + int subtype = _dbus_type_reader_get_current_type(&real->u.reader); + + _dbus_return_if_fail (_dbus_message_iter_check (real)); + _dbus_return_if_fail (value != NULL); + _dbus_return_if_fail ((subtype == DBUS_TYPE_INVALID) || + dbus_type_is_fixed (subtype)); + + _dbus_type_reader_read_fixed_multi (&real->u.reader, + value, n_elements); +} + +/** + * Initializes a #DBusMessageIter for appending arguments to the end + * of a message. + * + * @todo If appending any of the arguments fails due to lack of + * memory, the message is hosed and you have to start over building + * the whole message. + * + * @param message the message + * @param iter pointer to an iterator to initialize + */ +void +dbus_message_iter_init_append (DBusMessage *message, + DBusMessageIter *iter) +{ + DBusMessageRealIter *real = (DBusMessageRealIter *)iter; + + _dbus_return_if_fail (message != NULL); + _dbus_return_if_fail (iter != NULL); + + _dbus_message_iter_init_common (message, real, + DBUS_MESSAGE_ITER_TYPE_WRITER); + + /* We create the signature string and point iterators at it "on demand" + * when a value is actually appended. That means that init() never fails + * due to OOM. + */ + _dbus_type_writer_init_types_delayed (&real->u.writer, + message->byte_order, + &message->body, + _dbus_string_get_length (&message->body)); +} + +/** + * Creates a temporary signature string containing the current + * signature, stores it in the iterator, and points the iterator to + * the end of it. Used any time we write to the message. + * + * @param real an iterator without a type_str + * @returns #FALSE if no memory + */ +static dbus_bool_t +_dbus_message_iter_open_signature (DBusMessageRealIter *real) +{ + DBusString *str; + const DBusString *current_sig; + int current_sig_pos; + + _dbus_assert (real->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER); + + if (real->u.writer.type_str != NULL) + { + _dbus_assert (real->sig_refcount > 0); + real->sig_refcount += 1; + return TRUE; + } + + str = dbus_new (DBusString, 1); + if (str == NULL) + return FALSE; + + if (!_dbus_header_get_field_raw (&real->message->header, + DBUS_HEADER_FIELD_SIGNATURE, + ¤t_sig, ¤t_sig_pos)) + current_sig = NULL; + + if (current_sig) + { + int current_len; + + current_len = _dbus_string_get_byte (current_sig, current_sig_pos); + current_sig_pos += 1; /* move on to sig data */ + + if (!_dbus_string_init_preallocated (str, current_len + 4)) + { + dbus_free (str); + return FALSE; + } + + if (!_dbus_string_copy_len (current_sig, current_sig_pos, current_len, + str, 0)) + { + _dbus_string_free (str); + dbus_free (str); + return FALSE; + } + } + else + { + if (!_dbus_string_init_preallocated (str, 4)) + { + dbus_free (str); + return FALSE; + } + } + + real->sig_refcount = 1; + + _dbus_type_writer_add_types (&real->u.writer, + str, _dbus_string_get_length (str)); + return TRUE; +} + +/** + * Sets the new signature as the message signature, frees the + * signature string, and marks the iterator as not having a type_str + * anymore. Frees the signature even if it fails, so you can't + * really recover from failure. Kinda busted. + * + * @param real an iterator without a type_str + * @returns #FALSE if no memory + */ +static dbus_bool_t +_dbus_message_iter_close_signature (DBusMessageRealIter *real) +{ + DBusString *str; + const char *v_STRING; + dbus_bool_t retval; + + _dbus_assert (real->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER); + _dbus_assert (real->u.writer.type_str != NULL); + _dbus_assert (real->sig_refcount > 0); + + real->sig_refcount -= 1; + + if (real->sig_refcount > 0) + return TRUE; + _dbus_assert (real->sig_refcount == 0); + + retval = TRUE; + + str = real->u.writer.type_str; + + v_STRING = _dbus_string_get_const_data (str); + if (!_dbus_header_set_field_basic (&real->message->header, + DBUS_HEADER_FIELD_SIGNATURE, + DBUS_TYPE_SIGNATURE, + &v_STRING)) + retval = FALSE; + + _dbus_type_writer_remove_types (&real->u.writer); + _dbus_string_free (str); + dbus_free (str); + + return retval; +} + +#ifndef DBUS_DISABLE_CHECKS +static dbus_bool_t +_dbus_message_iter_append_check (DBusMessageRealIter *iter) +{ + if (!_dbus_message_iter_check (iter)) + return FALSE; + + if (iter->message->locked) + { + _dbus_warn_check_failed ("dbus append iterator can't be used: message is locked (has already been sent)\n"); + return FALSE; + } + + return TRUE; +} +#endif /* DBUS_DISABLE_CHECKS */ + +/** + * Appends a basic-typed value to the message. The basic types are the + * non-container types such as integer and string. + * + * The "value" argument should be the address of a basic-typed value. + * So for string, const char**. For integer, dbus_int32_t*. + * + * @todo If this fails due to lack of memory, the message is hosed and + * you have to start over building the whole message. + * + * @param iter the append iterator + * @param type the type of the value + * @param value the address of the value + * @returns #FALSE if not enough memory + */ +dbus_bool_t +dbus_message_iter_append_basic (DBusMessageIter *iter, + int type, + const void *value) +{ + DBusMessageRealIter *real = (DBusMessageRealIter *)iter; + dbus_bool_t ret; + + _dbus_return_val_if_fail (_dbus_message_iter_append_check (real), FALSE); + _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER, FALSE); + _dbus_return_val_if_fail (dbus_type_is_basic (type), FALSE); + _dbus_return_val_if_fail (value != NULL, FALSE); + + if (!_dbus_message_iter_open_signature (real)) + return FALSE; + + ret = _dbus_type_writer_write_basic (&real->u.writer, type, value); + + if (!_dbus_message_iter_close_signature (real)) + ret = FALSE; + + return ret; +} + +/** + * Appends a block of fixed-length values to an array. The + * fixed-length types are all basic types that are not string-like. So + * int32, double, bool, etc. You must call + * dbus_message_iter_open_container() to open an array of values + * before calling this function. You may call this function multiple + * times (and intermixed with calls to + * dbus_message_iter_append_basic()) for the same array. + * + * The "value" argument should be the address of the array. So for + * integer, "dbus_int32_t**" is expected for example. + * + * @warning in C, given "int array[]", "&array == array" (the + * comp.lang.c FAQ says otherwise, but gcc and the FAQ don't agree). + * So if you're using an array instead of a pointer you have to create + * a pointer variable, assign the array to it, then take the address + * of the pointer variable. + * @code + * const dbus_int32_t array[] = { 1, 2, 3 }; + * const dbus_int32_t *v_ARRAY = array; + * if (!dbus_message_iter_append_fixed_array (&iter, DBUS_TYPE_INT32, &v_ARRAY, 3)) + * fprintf (stderr, "No memory!\n"); + * @endcode + * For strings it works to write const char *array = "Hello" and then + * use &array though. + * + * @todo If this fails due to lack of memory, the message is hosed and + * you have to start over building the whole message. + * + * @param iter the append iterator + * @param element_type the type of the array elements + * @param value the address of the array + * @param n_elements the number of elements to append + * @returns #FALSE if not enough memory + */ +dbus_bool_t +dbus_message_iter_append_fixed_array (DBusMessageIter *iter, + int element_type, + const void *value, + int n_elements) +{ + DBusMessageRealIter *real = (DBusMessageRealIter *)iter; + dbus_bool_t ret; + + _dbus_return_val_if_fail (_dbus_message_iter_append_check (real), FALSE); + _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER, FALSE); + _dbus_return_val_if_fail (dbus_type_is_fixed (element_type), FALSE); + _dbus_return_val_if_fail (real->u.writer.container_type == DBUS_TYPE_ARRAY, FALSE); + _dbus_return_val_if_fail (value != NULL, FALSE); + _dbus_return_val_if_fail (n_elements >= 0, FALSE); + _dbus_return_val_if_fail (n_elements <= + DBUS_MAXIMUM_ARRAY_LENGTH / _dbus_type_get_alignment (element_type), + FALSE); + + ret = _dbus_type_writer_write_fixed_multi (&real->u.writer, element_type, value, n_elements); + + return ret; +} + +/** + * Appends a container-typed value to the message; you are required to + * append the contents of the container using the returned + * sub-iterator, and then call + * dbus_message_iter_close_container(). Container types are for + * example struct, variant, and array. For variants, the + * contained_signature should be the type of the single value inside + * the variant. For structs and dict entries, contained_signature + * should be #NULL; it will be set to whatever types you write into + * the struct. For arrays, contained_signature should be the type of + * the array elements. + * + * @todo If this fails due to lack of memory, the message is hosed and + * you have to start over building the whole message. + * + * @param iter the append iterator + * @param type the type of the value + * @param contained_signature the type of container contents + * @param sub sub-iterator to initialize + * @returns #FALSE if not enough memory + */ +dbus_bool_t +dbus_message_iter_open_container (DBusMessageIter *iter, + int type, + const char *contained_signature, + DBusMessageIter *sub) +{ + DBusMessageRealIter *real = (DBusMessageRealIter *)iter; + DBusMessageRealIter *real_sub = (DBusMessageRealIter *)sub; + DBusString contained_str; + + _dbus_return_val_if_fail (_dbus_message_iter_append_check (real), FALSE); + _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER, FALSE); + _dbus_return_val_if_fail (dbus_type_is_container (type), FALSE); + _dbus_return_val_if_fail (sub != NULL, FALSE); + _dbus_return_val_if_fail ((type == DBUS_TYPE_STRUCT && + contained_signature == NULL) || + (type == DBUS_TYPE_DICT_ENTRY && + contained_signature == NULL) || + (type == DBUS_TYPE_VARIANT && + contained_signature != NULL) || + (type == DBUS_TYPE_ARRAY && + contained_signature != NULL), FALSE); + + /* this would fail if the contained_signature is a dict entry, since + * dict entries are invalid signatures standalone (they must be in + * an array) + */ + _dbus_return_val_if_fail ((type == DBUS_TYPE_ARRAY && contained_signature && *contained_signature == DBUS_DICT_ENTRY_BEGIN_CHAR) || + (contained_signature == NULL || + _dbus_check_is_valid_signature (contained_signature)), + FALSE); + + if (!_dbus_message_iter_open_signature (real)) + return FALSE; + + *real_sub = *real; + + if (contained_signature != NULL) + { + _dbus_string_init_const (&contained_str, contained_signature); + + return _dbus_type_writer_recurse (&real->u.writer, + type, + &contained_str, 0, + &real_sub->u.writer); + } + else + { + return _dbus_type_writer_recurse (&real->u.writer, + type, + NULL, 0, + &real_sub->u.writer); + } +} + + +/** + * Closes a container-typed value appended to the message; may write + * out more information to the message known only after the entire + * container is written, and may free resources created by + * dbus_message_iter_open_container(). + * + * @todo If this fails due to lack of memory, the message is hosed and + * you have to start over building the whole message. + * + * @param iter the append iterator + * @param sub sub-iterator to close + * @returns #FALSE if not enough memory + */ +dbus_bool_t +dbus_message_iter_close_container (DBusMessageIter *iter, + DBusMessageIter *sub) +{ + DBusMessageRealIter *real = (DBusMessageRealIter *)iter; + DBusMessageRealIter *real_sub = (DBusMessageRealIter *)sub; + dbus_bool_t ret; + + _dbus_return_val_if_fail (_dbus_message_iter_append_check (real), FALSE); + _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER, FALSE); + _dbus_return_val_if_fail (_dbus_message_iter_append_check (real_sub), FALSE); + _dbus_return_val_if_fail (real_sub->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER, FALSE); + + ret = _dbus_type_writer_unrecurse (&real->u.writer, + &real_sub->u.writer); + + if (!_dbus_message_iter_close_signature (real)) + ret = FALSE; + + return ret; +} + +/** + * Sets a flag indicating that the message does not want a reply; if + * this flag is set, the other end of the connection may (but is not + * required to) optimize by not sending method return or error + * replies. If this flag is set, there is no way to know whether the + * message successfully arrived at the remote end. Normally you know a + * message was received when you receive the reply to it. + * + * The flag is #FALSE by default, that is by default the other end is + * required to reply. + * + * On the protocol level this toggles #DBUS_HEADER_FLAG_NO_REPLY_EXPECTED + * + * @param message the message + * @param no_reply #TRUE if no reply is desired + */ +void +dbus_message_set_no_reply (DBusMessage *message, + dbus_bool_t no_reply) +{ + _dbus_return_if_fail (message != NULL); + _dbus_return_if_fail (!message->locked); + + _dbus_header_toggle_flag (&message->header, + DBUS_HEADER_FLAG_NO_REPLY_EXPECTED, + no_reply); +} + +/** + * Returns #TRUE if the message does not expect + * a reply. + * + * @param message the message + * @returns #TRUE if the message sender isn't waiting for a reply + */ +dbus_bool_t +dbus_message_get_no_reply (DBusMessage *message) +{ + _dbus_return_val_if_fail (message != NULL, FALSE); + + return _dbus_header_get_flag (&message->header, + DBUS_HEADER_FLAG_NO_REPLY_EXPECTED); +} + +/** + * Sets a flag indicating that an owner for the destination name will + * be automatically started before the message is delivered. When this + * flag is set, the message is held until a name owner finishes + * starting up, or fails to start up. In case of failure, the reply + * will be an error. + * + * The flag is set to #TRUE by default, i.e. auto starting is the default. + * + * On the protocol level this toggles #DBUS_HEADER_FLAG_NO_AUTO_START + * + * @param message the message + * @param auto_start #TRUE if auto-starting is desired + */ +void +dbus_message_set_auto_start (DBusMessage *message, + dbus_bool_t auto_start) +{ + _dbus_return_if_fail (message != NULL); + _dbus_return_if_fail (!message->locked); + + _dbus_header_toggle_flag (&message->header, + DBUS_HEADER_FLAG_NO_AUTO_START, + !auto_start); +} + +/** + * Returns #TRUE if the message will cause an owner for + * destination name to be auto-started. + * + * @param message the message + * @returns #TRUE if the message will use auto-start + */ +dbus_bool_t +dbus_message_get_auto_start (DBusMessage *message) +{ + _dbus_return_val_if_fail (message != NULL, FALSE); + + return !_dbus_header_get_flag (&message->header, + DBUS_HEADER_FLAG_NO_AUTO_START); +} + + +/** + * Sets the object path this message is being sent to (for + * DBUS_MESSAGE_TYPE_METHOD_CALL) or the one a signal is being + * emitted from (for DBUS_MESSAGE_TYPE_SIGNAL). + * + * The path must contain only valid characters as defined + * in the D-Bus specification. + * + * @param message the message + * @param object_path the path or #NULL to unset + * @returns #FALSE if not enough memory + */ +dbus_bool_t +dbus_message_set_path (DBusMessage *message, + const char *object_path) +{ + _dbus_return_val_if_fail (message != NULL, FALSE); + _dbus_return_val_if_fail (!message->locked, FALSE); + _dbus_return_val_if_fail (object_path == NULL || + _dbus_check_is_valid_path (object_path), + FALSE); + + return set_or_delete_string_field (message, + DBUS_HEADER_FIELD_PATH, + DBUS_TYPE_OBJECT_PATH, + object_path); +} + +/** + * Gets the object path this message is being sent to (for + * DBUS_MESSAGE_TYPE_METHOD_CALL) or being emitted from (for + * DBUS_MESSAGE_TYPE_SIGNAL). Returns #NULL if none. + * + * See also dbus_message_get_path_decomposed(). + * + * The returned string becomes invalid if the message is + * modified, since it points into the wire-marshaled message data. + * + * @param message the message + * @returns the path (should not be freed) or #NULL + */ +const char* +dbus_message_get_path (DBusMessage *message) +{ + const char *v; + + _dbus_return_val_if_fail (message != NULL, NULL); + + v = NULL; /* in case field doesn't exist */ + _dbus_header_get_field_basic (&message->header, + DBUS_HEADER_FIELD_PATH, + DBUS_TYPE_OBJECT_PATH, + &v); + return v; +} + +/** + * Checks if the message has a particular object path. The object + * path is the destination object for a method call or the emitting + * object for a signal. + * + * @param message the message + * @param path the path name + * @returns #TRUE if there is a path field in the header + */ +dbus_bool_t +dbus_message_has_path (DBusMessage *message, + const char *path) +{ + const char *msg_path; + msg_path = dbus_message_get_path (message); + + if (msg_path == NULL) + { + if (path == NULL) + return TRUE; + else + return FALSE; + } + + if (path == NULL) + return FALSE; + + if (strcmp (msg_path, path) == 0) + return TRUE; + + return FALSE; +} + +/** + * Gets the object path this message is being sent to + * (for DBUS_MESSAGE_TYPE_METHOD_CALL) or being emitted + * from (for DBUS_MESSAGE_TYPE_SIGNAL) in a decomposed + * format (one array element per path component). + * Free the returned array with dbus_free_string_array(). + * + * An empty but non-NULL path array means the path "/". + * So the path "/foo/bar" becomes { "foo", "bar", NULL } + * and the path "/" becomes { NULL }. + * + * See also dbus_message_get_path(). + * + * @todo this could be optimized by using the len from the message + * instead of calling strlen() again + * + * @param message the message + * @param path place to store allocated array of path components; #NULL set here if no path field exists + * @returns #FALSE if no memory to allocate the array + */ +dbus_bool_t +dbus_message_get_path_decomposed (DBusMessage *message, + char ***path) +{ + const char *v; + + _dbus_return_val_if_fail (message != NULL, FALSE); + _dbus_return_val_if_fail (path != NULL, FALSE); + + *path = NULL; + + v = dbus_message_get_path (message); + if (v != NULL) + { + if (!_dbus_decompose_path (v, strlen (v), + path, NULL)) + return FALSE; + } + return TRUE; +} + +/** + * Sets the interface this message is being sent to + * (for DBUS_MESSAGE_TYPE_METHOD_CALL) or + * the interface a signal is being emitted from + * (for DBUS_MESSAGE_TYPE_SIGNAL). + * + * The interface name must contain only valid characters as defined + * in the D-Bus specification. + * + * @param message the message + * @param interface the interface or #NULL to unset + * @returns #FALSE if not enough memory + */ +dbus_bool_t +dbus_message_set_interface (DBusMessage *message, + const char *interface) +{ + _dbus_return_val_if_fail (message != NULL, FALSE); + _dbus_return_val_if_fail (!message->locked, FALSE); + _dbus_return_val_if_fail (interface == NULL || + _dbus_check_is_valid_interface (interface), + FALSE); + + return set_or_delete_string_field (message, + DBUS_HEADER_FIELD_INTERFACE, + DBUS_TYPE_STRING, + interface); +} + +/** + * Gets the interface this message is being sent to + * (for DBUS_MESSAGE_TYPE_METHOD_CALL) or being emitted + * from (for DBUS_MESSAGE_TYPE_SIGNAL). + * The interface name is fully-qualified (namespaced). + * Returns #NULL if none. + * + * The returned string becomes invalid if the message is + * modified, since it points into the wire-marshaled message data. + * + * @param message the message + * @returns the message interface (should not be freed) or #NULL + */ +const char* +dbus_message_get_interface (DBusMessage *message) +{ + const char *v; + + _dbus_return_val_if_fail (message != NULL, NULL); + + v = NULL; /* in case field doesn't exist */ + _dbus_header_get_field_basic (&message->header, + DBUS_HEADER_FIELD_INTERFACE, + DBUS_TYPE_STRING, + &v); + return v; +} + +/** + * Checks if the message has an interface + * + * @param message the message + * @param interface the interface name + * @returns #TRUE if the interface field in the header matches + */ +dbus_bool_t +dbus_message_has_interface (DBusMessage *message, + const char *interface) +{ + const char *msg_interface; + msg_interface = dbus_message_get_interface (message); + + if (msg_interface == NULL) + { + if (interface == NULL) + return TRUE; + else + return FALSE; + } + + if (interface == NULL) + return FALSE; + + if (strcmp (msg_interface, interface) == 0) + return TRUE; + + return FALSE; + +} + +/** + * Sets the interface member being invoked + * (DBUS_MESSAGE_TYPE_METHOD_CALL) or emitted + * (DBUS_MESSAGE_TYPE_SIGNAL). + * + * The member name must contain only valid characters as defined + * in the D-Bus specification. + * + * @param message the message + * @param member the member or #NULL to unset + * @returns #FALSE if not enough memory + */ +dbus_bool_t +dbus_message_set_member (DBusMessage *message, + const char *member) +{ + _dbus_return_val_if_fail (message != NULL, FALSE); + _dbus_return_val_if_fail (!message->locked, FALSE); + _dbus_return_val_if_fail (member == NULL || + _dbus_check_is_valid_member (member), + FALSE); + + return set_or_delete_string_field (message, + DBUS_HEADER_FIELD_MEMBER, + DBUS_TYPE_STRING, + member); +} + +/** + * Gets the interface member being invoked + * (DBUS_MESSAGE_TYPE_METHOD_CALL) or emitted + * (DBUS_MESSAGE_TYPE_SIGNAL). Returns #NULL if none. + * + * The returned string becomes invalid if the message is + * modified, since it points into the wire-marshaled message data. + * + * @param message the message + * @returns the member name (should not be freed) or #NULL + */ +const char* +dbus_message_get_member (DBusMessage *message) +{ + const char *v; + + _dbus_return_val_if_fail (message != NULL, NULL); + + v = NULL; /* in case field doesn't exist */ + _dbus_header_get_field_basic (&message->header, + DBUS_HEADER_FIELD_MEMBER, + DBUS_TYPE_STRING, + &v); + return v; +} + +/** + * Checks if the message has an interface member + * + * @param message the message + * @param member the member name + * @returns #TRUE if there is a member field in the header + */ +dbus_bool_t +dbus_message_has_member (DBusMessage *message, + const char *member) +{ + const char *msg_member; + msg_member = dbus_message_get_member (message); + + if (msg_member == NULL) + { + if (member == NULL) + return TRUE; + else + return FALSE; + } + + if (member == NULL) + return FALSE; + + if (strcmp (msg_member, member) == 0) + return TRUE; + + return FALSE; + +} + +/** + * Sets the name of the error (DBUS_MESSAGE_TYPE_ERROR). + * The name is fully-qualified (namespaced). + * + * The error name must contain only valid characters as defined + * in the D-Bus specification. + * + * @param message the message + * @param error_name the name or #NULL to unset + * @returns #FALSE if not enough memory + */ +dbus_bool_t +dbus_message_set_error_name (DBusMessage *message, + const char *error_name) +{ + _dbus_return_val_if_fail (message != NULL, FALSE); + _dbus_return_val_if_fail (!message->locked, FALSE); + _dbus_return_val_if_fail (error_name == NULL || + _dbus_check_is_valid_error_name (error_name), + FALSE); + + return set_or_delete_string_field (message, + DBUS_HEADER_FIELD_ERROR_NAME, + DBUS_TYPE_STRING, + error_name); +} + +/** + * Gets the error name (DBUS_MESSAGE_TYPE_ERROR only) + * or #NULL if none. + * + * The returned string becomes invalid if the message is + * modified, since it points into the wire-marshaled message data. + * + * @param message the message + * @returns the error name (should not be freed) or #NULL + */ +const char* +dbus_message_get_error_name (DBusMessage *message) +{ + const char *v; + + _dbus_return_val_if_fail (message != NULL, NULL); + + v = NULL; /* in case field doesn't exist */ + _dbus_header_get_field_basic (&message->header, + DBUS_HEADER_FIELD_ERROR_NAME, + DBUS_TYPE_STRING, + &v); + return v; +} + +/** + * Sets the message's destination. The destination is the name of + * another connection on the bus and may be either the unique name + * assigned by the bus to each connection, or a well-known name + * specified in advance. + * + * The destination name must contain only valid characters as defined + * in the D-Bus specification. + * + * @param message the message + * @param destination the destination name or #NULL to unset + * @returns #FALSE if not enough memory + */ +dbus_bool_t +dbus_message_set_destination (DBusMessage *message, + const char *destination) +{ + _dbus_return_val_if_fail (message != NULL, FALSE); + _dbus_return_val_if_fail (!message->locked, FALSE); + _dbus_return_val_if_fail (destination == NULL || + _dbus_check_is_valid_bus_name (destination), + FALSE); + + return set_or_delete_string_field (message, + DBUS_HEADER_FIELD_DESTINATION, + DBUS_TYPE_STRING, + destination); +} + +/** + * Gets the destination of a message or #NULL if there is none set. + * + * The returned string becomes invalid if the message is + * modified, since it points into the wire-marshaled message data. + * + * @param message the message + * @returns the message destination (should not be freed) or #NULL + */ +const char* +dbus_message_get_destination (DBusMessage *message) +{ + const char *v; + + _dbus_return_val_if_fail (message != NULL, NULL); + + v = NULL; /* in case field doesn't exist */ + _dbus_header_get_field_basic (&message->header, + DBUS_HEADER_FIELD_DESTINATION, + DBUS_TYPE_STRING, + &v); + return v; +} + +/** + * Sets the message sender. + * + * The sender must be a valid bus name as defined in the D-Bus + * specification. + * + * Usually you don't want to call this. The message bus daemon will + * call it to set the origin of each message. If you aren't implementing + * a message bus daemon you shouldn't need to set the sender. + * + * @param message the message + * @param sender the sender or #NULL to unset + * @returns #FALSE if not enough memory + */ +dbus_bool_t +dbus_message_set_sender (DBusMessage *message, + const char *sender) +{ + _dbus_return_val_if_fail (message != NULL, FALSE); + _dbus_return_val_if_fail (!message->locked, FALSE); + _dbus_return_val_if_fail (sender == NULL || + _dbus_check_is_valid_bus_name (sender), + FALSE); + + return set_or_delete_string_field (message, + DBUS_HEADER_FIELD_SENDER, + DBUS_TYPE_STRING, + sender); +} + +/** + * Gets the unique name of the connection which originated this + * message, or #NULL if unknown or inapplicable. The sender is filled + * in by the message bus. + * + * Note, the returned sender is always the unique bus name. + * Connections may own multiple other bus names, but those + * are not found in the sender field. + * + * The returned string becomes invalid if the message is + * modified, since it points into the wire-marshaled message data. + * + * @param message the message + * @returns the unique name of the sender or #NULL + */ +const char* +dbus_message_get_sender (DBusMessage *message) +{ + const char *v; + + _dbus_return_val_if_fail (message != NULL, NULL); + + v = NULL; /* in case field doesn't exist */ + _dbus_header_get_field_basic (&message->header, + DBUS_HEADER_FIELD_SENDER, + DBUS_TYPE_STRING, + &v); + return v; +} + +/** + * Gets the type signature of the message, i.e. the arguments in the + * message payload. The signature includes only "in" arguments for + * #DBUS_MESSAGE_TYPE_METHOD_CALL and only "out" arguments for + * #DBUS_MESSAGE_TYPE_METHOD_RETURN, so is slightly different from + * what you might expect (that is, it does not include the signature of the + * entire C++-style method). + * + * The signature is a string made up of type codes such as + * #DBUS_TYPE_INT32. The string is terminated with nul (nul is also + * the value of #DBUS_TYPE_INVALID). + * + * The returned string becomes invalid if the message is + * modified, since it points into the wire-marshaled message data. + * + * @param message the message + * @returns the type signature + */ +const char* +dbus_message_get_signature (DBusMessage *message) +{ + const DBusString *type_str; + int type_pos; + + _dbus_return_val_if_fail (message != NULL, NULL); + + get_const_signature (&message->header, &type_str, &type_pos); + + return _dbus_string_get_const_data_len (type_str, type_pos, 0); +} + +static dbus_bool_t +_dbus_message_has_type_interface_member (DBusMessage *message, + int type, + const char *interface, + const char *member) +{ + const char *n; + + _dbus_assert (message != NULL); + _dbus_assert (interface != NULL); + _dbus_assert (member != NULL); + + if (dbus_message_get_type (message) != type) + return FALSE; + + /* Optimize by checking the short member name first + * instead of the longer interface name + */ + + n = dbus_message_get_member (message); + + if (n && strcmp (n, member) == 0) + { + n = dbus_message_get_interface (message); + + if (n == NULL || strcmp (n, interface) == 0) + return TRUE; + } + + return FALSE; +} + +/** + * Checks whether the message is a method call with the given + * interface and member fields. If the message is not + * #DBUS_MESSAGE_TYPE_METHOD_CALL, or has a different interface or + * member field, returns #FALSE. If the interface field is missing, + * then it will be assumed equal to the provided interface. The D-Bus + * protocol allows method callers to leave out the interface name. + * + * @param message the message + * @param interface the name to check (must not be #NULL) + * @param method the name to check (must not be #NULL) + * + * @returns #TRUE if the message is the specified method call + */ +dbus_bool_t +dbus_message_is_method_call (DBusMessage *message, + const char *interface, + const char *method) +{ + _dbus_return_val_if_fail (message != NULL, FALSE); + _dbus_return_val_if_fail (interface != NULL, FALSE); + _dbus_return_val_if_fail (method != NULL, FALSE); + /* don't check that interface/method are valid since it would be + * expensive, and not catch many common errors + */ + + return _dbus_message_has_type_interface_member (message, + DBUS_MESSAGE_TYPE_METHOD_CALL, + interface, method); +} + +/** + * Checks whether the message is a signal with the given interface and + * member fields. If the message is not #DBUS_MESSAGE_TYPE_SIGNAL, or + * has a different interface or member field, returns #FALSE. + * + * @param message the message + * @param interface the name to check (must not be #NULL) + * @param signal_name the name to check (must not be #NULL) + * + * @returns #TRUE if the message is the specified signal + */ +dbus_bool_t +dbus_message_is_signal (DBusMessage *message, + const char *interface, + const char *signal_name) +{ + _dbus_return_val_if_fail (message != NULL, FALSE); + _dbus_return_val_if_fail (interface != NULL, FALSE); + _dbus_return_val_if_fail (signal_name != NULL, FALSE); + /* don't check that interface/name are valid since it would be + * expensive, and not catch many common errors + */ + + return _dbus_message_has_type_interface_member (message, + DBUS_MESSAGE_TYPE_SIGNAL, + interface, signal_name); +} + +/** + * Checks whether the message is an error reply with the given error + * name. If the message is not #DBUS_MESSAGE_TYPE_ERROR, or has a + * different name, returns #FALSE. + * + * @param message the message + * @param error_name the name to check (must not be #NULL) + * + * @returns #TRUE if the message is the specified error + */ +dbus_bool_t +dbus_message_is_error (DBusMessage *message, + const char *error_name) +{ + const char *n; + + _dbus_return_val_if_fail (message != NULL, FALSE); + _dbus_return_val_if_fail (error_name != NULL, FALSE); + /* don't check that error_name is valid since it would be expensive, + * and not catch many common errors + */ + + if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_ERROR) + return FALSE; + + n = dbus_message_get_error_name (message); + + if (n && strcmp (n, error_name) == 0) + return TRUE; + else + return FALSE; +} + +/** + * Checks whether the message was sent to the given name. If the + * message has no destination specified or has a different + * destination, returns #FALSE. + * + * @param message the message + * @param name the name to check (must not be #NULL) + * + * @returns #TRUE if the message has the given destination name + */ +dbus_bool_t +dbus_message_has_destination (DBusMessage *message, + const char *name) +{ + const char *s; + + _dbus_return_val_if_fail (message != NULL, FALSE); + _dbus_return_val_if_fail (name != NULL, FALSE); + /* don't check that name is valid since it would be expensive, and + * not catch many common errors + */ + + s = dbus_message_get_destination (message); + + if (s && strcmp (s, name) == 0) + return TRUE; + else + return FALSE; +} + +/** + * Checks whether the message has the given unique name as its sender. + * If the message has no sender specified or has a different sender, + * returns #FALSE. Note that a peer application will always have the + * unique name of the connection as the sender. So you can't use this + * function to see whether a sender owned a well-known name. + * + * Messages from the bus itself will have #DBUS_SERVICE_DBUS + * as the sender. + * + * @param message the message + * @param name the name to check (must not be #NULL) + * + * @returns #TRUE if the message has the given sender + */ +dbus_bool_t +dbus_message_has_sender (DBusMessage *message, + const char *name) +{ + const char *s; + + _dbus_return_val_if_fail (message != NULL, FALSE); + _dbus_return_val_if_fail (name != NULL, FALSE); + /* don't check that name is valid since it would be expensive, and + * not catch many common errors + */ + + s = dbus_message_get_sender (message); + + if (s && strcmp (s, name) == 0) + return TRUE; + else + return FALSE; +} + +/** + * Checks whether the message has the given signature; see + * dbus_message_get_signature() for more details on what the signature + * looks like. + * + * @param message the message + * @param signature typecode array + * @returns #TRUE if message has the given signature +*/ +dbus_bool_t +dbus_message_has_signature (DBusMessage *message, + const char *signature) +{ + const char *s; + + _dbus_return_val_if_fail (message != NULL, FALSE); + _dbus_return_val_if_fail (signature != NULL, FALSE); + /* don't check that signature is valid since it would be expensive, + * and not catch many common errors + */ + + s = dbus_message_get_signature (message); + + if (s && strcmp (s, signature) == 0) + return TRUE; + else + return FALSE; +} + +/** + * Sets a #DBusError based on the contents of the given + * message. The error is only set if the message + * is an error message, as in #DBUS_MESSAGE_TYPE_ERROR. + * The name of the error is set to the name of the message, + * and the error message is set to the first argument + * if the argument exists and is a string. + * + * The return value indicates whether the error was set (the error is + * set if and only if the message is an error message). So you can + * check for an error reply and convert it to DBusError in one go: + * @code + * if (dbus_set_error_from_message (error, reply)) + * return error; + * else + * process reply; + * @endcode + * + * @param error the error to set + * @param message the message to set it from + * @returns #TRUE if the message had type #DBUS_MESSAGE_TYPE_ERROR + */ +dbus_bool_t +dbus_set_error_from_message (DBusError *error, + DBusMessage *message) +{ + const char *str; + + _dbus_return_val_if_fail (message != NULL, FALSE); + _dbus_return_val_if_error_is_set (error, FALSE); + + if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_ERROR) + return FALSE; + + str = NULL; + dbus_message_get_args (message, NULL, + DBUS_TYPE_STRING, &str, + DBUS_TYPE_INVALID); + + dbus_set_error (error, dbus_message_get_error_name (message), + str ? "%s" : NULL, str); + + return TRUE; +} + +/** @} */ + +/** + * @addtogroup DBusMessageInternals + * + * @{ + */ + +/** + * The initial buffer size of the message loader. + * + * @todo this should be based on min header size plus some average + * body size, or something. Or rather, the min header size only, if we + * want to try to read only the header, store that in a DBusMessage, + * then read only the body and store that, etc., depends on + * how we optimize _dbus_message_loader_get_buffer() and what + * the exact message format is. + */ +#define INITIAL_LOADER_DATA_LEN 32 + +/** + * Creates a new message loader. Returns #NULL if memory can't + * be allocated. + * + * @returns new loader, or #NULL. + */ +DBusMessageLoader* +_dbus_message_loader_new (void) +{ + DBusMessageLoader *loader; + + loader = dbus_new0 (DBusMessageLoader, 1); + if (loader == NULL) + return NULL; + + loader->refcount = 1; + + loader->corrupted = FALSE; + loader->corruption_reason = DBUS_VALID; + + /* this can be configured by the app, but defaults to the protocol max */ + loader->max_message_size = DBUS_MAXIMUM_MESSAGE_LENGTH; + + if (!_dbus_string_init (&loader->data)) + { + dbus_free (loader); + return NULL; + } + + /* preallocate the buffer for speed, ignore failure */ + _dbus_string_set_length (&loader->data, INITIAL_LOADER_DATA_LEN); + _dbus_string_set_length (&loader->data, 0); + + return loader; +} + +/** + * Increments the reference count of the loader. + * + * @param loader the loader. + * @returns the loader + */ +DBusMessageLoader * +_dbus_message_loader_ref (DBusMessageLoader *loader) +{ + loader->refcount += 1; + + return loader; +} + +/** + * Decrements the reference count of the loader and finalizes the + * loader when the count reaches zero. + * + * @param loader the loader. + */ +void +_dbus_message_loader_unref (DBusMessageLoader *loader) +{ + loader->refcount -= 1; + if (loader->refcount == 0) + { + _dbus_list_foreach (&loader->messages, + (DBusForeachFunction) dbus_message_unref, + NULL); + _dbus_list_clear (&loader->messages); + _dbus_string_free (&loader->data); + dbus_free (loader); + } +} + +/** + * Gets the buffer to use for reading data from the network. Network + * data is read directly into an allocated buffer, which is then used + * in the DBusMessage, to avoid as many extra memcpy's as possible. + * The buffer must always be returned immediately using + * _dbus_message_loader_return_buffer(), even if no bytes are + * successfully read. + * + * @todo this function can be a lot more clever. For example + * it can probably always return a buffer size to read exactly + * the body of the next message, thus avoiding any memory wastage + * or reallocs. + * + * @todo we need to enforce a max length on strings in header fields. + * + * @param loader the message loader. + * @param buffer the buffer + */ +void +_dbus_message_loader_get_buffer (DBusMessageLoader *loader, + DBusString **buffer) +{ + _dbus_assert (!loader->buffer_outstanding); + + *buffer = &loader->data; + + loader->buffer_outstanding = TRUE; +} + +/** + * Returns a buffer obtained from _dbus_message_loader_get_buffer(), + * indicating to the loader how many bytes of the buffer were filled + * in. This function must always be called, even if no bytes were + * successfully read. + * + * @param loader the loader. + * @param buffer the buffer. + * @param bytes_read number of bytes that were read into the buffer. + */ +void +_dbus_message_loader_return_buffer (DBusMessageLoader *loader, + DBusString *buffer, + int bytes_read) +{ + _dbus_assert (loader->buffer_outstanding); + _dbus_assert (buffer == &loader->data); + + loader->buffer_outstanding = FALSE; +} + +/* + * FIXME when we move the header out of the buffer, that memmoves all + * buffered messages. Kind of crappy. + * + * Also we copy the header and body, which is kind of crappy. To + * avoid this, we have to allow header and body to be in a single + * memory block, which is good for messages we read and bad for + * messages we are creating. But we could move_len() the buffer into + * this single memory block, and move_len() will just swap the buffers + * if you're moving the entire buffer replacing the dest string. + * + * We could also have the message loader tell the transport how many + * bytes to read; so it would first ask for some arbitrary number like + * 256, then if the message was incomplete it would use the + * header/body len to ask for exactly the size of the message (or + * blocks the size of a typical kernel buffer for the socket). That + * way we don't get trailing bytes in the buffer that have to be + * memmoved. Though I suppose we also don't have a chance of reading a + * bunch of small messages at once, so the optimization may be stupid. + * + * Another approach would be to keep a "start" index into + * loader->data and only delete it occasionally, instead of after + * each message is loaded. + * + * load_message() returns FALSE if not enough memory OR the loader was corrupted + */ +static dbus_bool_t +load_message (DBusMessageLoader *loader, + DBusMessage *message, + int byte_order, + int fields_array_len, + int header_len, + int body_len) +{ + dbus_bool_t oom; + DBusValidity validity; + const DBusString *type_str; + int type_pos; + DBusValidationMode mode; + + mode = DBUS_VALIDATION_MODE_DATA_IS_UNTRUSTED; + + oom = FALSE; + +#if 0 + _dbus_verbose_bytes_of_string (&loader->data, 0, header_len /* + body_len */); +#endif + + /* 1. VALIDATE AND COPY OVER HEADER */ + _dbus_assert (_dbus_string_get_length (&message->header.data) == 0); + _dbus_assert ((header_len + body_len) <= _dbus_string_get_length (&loader->data)); + + if (!_dbus_header_load (&message->header, + mode, + &validity, + byte_order, + fields_array_len, + header_len, + body_len, + &loader->data, 0, + _dbus_string_get_length (&loader->data))) + { + _dbus_verbose ("Failed to load header for new message code %d\n", validity); + + /* assert here so we can catch any code that still uses DBUS_VALID to indicate + oom errors. They should use DBUS_VALIDITY_UNKNOWN_OOM_ERROR instead */ + _dbus_assert (validity != DBUS_VALID); + + if (validity == DBUS_VALIDITY_UNKNOWN_OOM_ERROR) + oom = TRUE; + else + { + loader->corrupted = TRUE; + loader->corruption_reason = validity; + } + goto failed; + } + + _dbus_assert (validity == DBUS_VALID); + + message->byte_order = byte_order; + + /* 2. VALIDATE BODY */ + if (mode != DBUS_VALIDATION_MODE_WE_TRUST_THIS_DATA_ABSOLUTELY) + { + get_const_signature (&message->header, &type_str, &type_pos); + + /* Because the bytes_remaining arg is NULL, this validates that the + * body is the right length + */ + validity = _dbus_validate_body_with_reason (type_str, + type_pos, + byte_order, + NULL, + &loader->data, + header_len, + body_len); + if (validity != DBUS_VALID) + { + _dbus_verbose ("Failed to validate message body code %d\n", validity); + + loader->corrupted = TRUE; + loader->corruption_reason = validity; + + goto failed; + } + } + + /* 3. COPY OVER BODY AND QUEUE MESSAGE */ + + if (!_dbus_list_append (&loader->messages, message)) + { + _dbus_verbose ("Failed to append new message to loader queue\n"); + oom = TRUE; + goto failed; + } + + _dbus_assert (_dbus_string_get_length (&message->body) == 0); + _dbus_assert (_dbus_string_get_length (&loader->data) >= + (header_len + body_len)); + + if (!_dbus_string_copy_len (&loader->data, header_len, body_len, &message->body, 0)) + { + _dbus_verbose ("Failed to move body into new message\n"); + oom = TRUE; + goto failed; + } + + _dbus_string_delete (&loader->data, 0, header_len + body_len); + + /* don't waste more than 2k of memory */ + _dbus_string_compact (&loader->data, 2048); + + _dbus_assert (_dbus_string_get_length (&message->header.data) == header_len); + _dbus_assert (_dbus_string_get_length (&message->body) == body_len); + + _dbus_verbose ("Loaded message %p\n", message); + + _dbus_assert (!oom); + _dbus_assert (!loader->corrupted); + _dbus_assert (loader->messages != NULL); + _dbus_assert (_dbus_list_find_last (&loader->messages, message) != NULL); + + return TRUE; + + failed: + + /* Clean up */ + + /* does nothing if the message isn't in the list */ + _dbus_list_remove_last (&loader->messages, message); + + if (oom) + _dbus_assert (!loader->corrupted); + else + _dbus_assert (loader->corrupted); + + _dbus_verbose_bytes_of_string (&loader->data, 0, _dbus_string_get_length (&loader->data)); + + return FALSE; +} + +/** + * Converts buffered data into messages, if we have enough data. If + * we don't have enough data, does nothing. + * + * @todo we need to check that the proper named header fields exist + * for each message type. + * + * @todo If a message has unknown type, we should probably eat it + * right here rather than passing it out to applications. However + * it's not an error to see messages of unknown type. + * + * @param loader the loader. + * @returns #TRUE if we had enough memory to finish. + */ +dbus_bool_t +_dbus_message_loader_queue_messages (DBusMessageLoader *loader) +{ + while (!loader->corrupted && + _dbus_string_get_length (&loader->data) >= DBUS_MINIMUM_HEADER_SIZE) + { + DBusValidity validity; + int byte_order, fields_array_len, header_len, body_len; + + if (_dbus_header_have_message_untrusted (loader->max_message_size, + &validity, + &byte_order, + &fields_array_len, + &header_len, + &body_len, + &loader->data, 0, + _dbus_string_get_length (&loader->data))) + { + DBusMessage *message; + + _dbus_assert (validity == DBUS_VALID); + + message = dbus_message_new_empty_header (); + if (message == NULL) + return FALSE; + + if (!load_message (loader, message, + byte_order, fields_array_len, + header_len, body_len)) + { + dbus_message_unref (message); + /* load_message() returns false if corrupted or OOM; if + * corrupted then return TRUE for not OOM + */ + return loader->corrupted; + } + + _dbus_assert (loader->messages != NULL); + _dbus_assert (_dbus_list_find_last (&loader->messages, message) != NULL); + } + else + { + _dbus_verbose ("Initial peek at header says we don't have a whole message yet, or data broken with invalid code %d\n", + validity); + if (validity != DBUS_VALID) + { + loader->corrupted = TRUE; + loader->corruption_reason = validity; + } + return TRUE; + } + } + + return TRUE; +} + +/** + * Peeks at first loaded message, returns #NULL if no messages have + * been queued. + * + * @param loader the loader. + * @returns the next message, or #NULL if none. + */ +DBusMessage* +_dbus_message_loader_peek_message (DBusMessageLoader *loader) +{ + if (loader->messages) + return loader->messages->data; + else + return NULL; +} + +/** + * Pops a loaded message (passing ownership of the message + * to the caller). Returns #NULL if no messages have been + * queued. + * + * @param loader the loader. + * @returns the next message, or #NULL if none. + */ +DBusMessage* +_dbus_message_loader_pop_message (DBusMessageLoader *loader) +{ + return _dbus_list_pop_first (&loader->messages); +} + +/** + * Pops a loaded message inside a list link (passing ownership of the + * message and link to the caller). Returns #NULL if no messages have + * been loaded. + * + * @param loader the loader. + * @returns the next message link, or #NULL if none. + */ +DBusList* +_dbus_message_loader_pop_message_link (DBusMessageLoader *loader) +{ + return _dbus_list_pop_first_link (&loader->messages); +} + +/** + * Returns a popped message link, used to undo a pop. + * + * @param loader the loader + * @param link the link with a message in it + */ +void +_dbus_message_loader_putback_message_link (DBusMessageLoader *loader, + DBusList *link) +{ + _dbus_list_prepend_link (&loader->messages, link); +} + +/** + * Checks whether the loader is confused due to bad data. + * If messages are received that are invalid, the + * loader gets confused and gives up permanently. + * This state is called "corrupted." + * + * @param loader the loader + * @returns #TRUE if the loader is hosed. + */ +dbus_bool_t +_dbus_message_loader_get_is_corrupted (DBusMessageLoader *loader) +{ + _dbus_assert ((loader->corrupted && loader->corruption_reason != DBUS_VALID) || + (!loader->corrupted && loader->corruption_reason == DBUS_VALID)); + return loader->corrupted; +} + +/** + * Sets the maximum size message we allow. + * + * @param loader the loader + * @param size the max message size in bytes + */ +void +_dbus_message_loader_set_max_message_size (DBusMessageLoader *loader, + long size) +{ + if (size > DBUS_MAXIMUM_MESSAGE_LENGTH) + { + _dbus_verbose ("clamping requested max message size %ld to %d\n", + size, DBUS_MAXIMUM_MESSAGE_LENGTH); + size = DBUS_MAXIMUM_MESSAGE_LENGTH; + } + loader->max_message_size = size; +} + +/** + * Gets the maximum allowed message size in bytes. + * + * @param loader the loader + * @returns max size in bytes + */ +long +_dbus_message_loader_get_max_message_size (DBusMessageLoader *loader) +{ + return loader->max_message_size; +} + +static DBusDataSlotAllocator slot_allocator; +_DBUS_DEFINE_GLOBAL_LOCK (message_slots); + +/** + * Allocates an integer ID to be used for storing application-specific + * data on any DBusMessage. The allocated ID may then be used + * with dbus_message_set_data() and dbus_message_get_data(). + * The passed-in slot must be initialized to -1, and is filled in + * with the slot ID. If the passed-in slot is not -1, it's assumed + * to be already allocated, and its refcount is incremented. + * + * The allocated slot is global, i.e. all DBusMessage objects will + * have a slot with the given integer ID reserved. + * + * @param slot_p address of a global variable storing the slot + * @returns #FALSE on failure (no memory) + */ +dbus_bool_t +dbus_message_allocate_data_slot (dbus_int32_t *slot_p) +{ + return _dbus_data_slot_allocator_alloc (&slot_allocator, + &_DBUS_LOCK_NAME (message_slots), + slot_p); +} + +/** + * Deallocates a global ID for message data slots. + * dbus_message_get_data() and dbus_message_set_data() may no + * longer be used with this slot. Existing data stored on existing + * DBusMessage objects will be freed when the message is + * finalized, but may not be retrieved (and may only be replaced if + * someone else reallocates the slot). When the refcount on the + * passed-in slot reaches 0, it is set to -1. + * + * @param slot_p address storing the slot to deallocate + */ +void +dbus_message_free_data_slot (dbus_int32_t *slot_p) +{ + _dbus_return_if_fail (*slot_p >= 0); + + _dbus_data_slot_allocator_free (&slot_allocator, slot_p); +} + +/** + * Stores a pointer on a DBusMessage, along + * with an optional function to be used for freeing + * the data when the data is set again, or when + * the message is finalized. The slot number + * must have been allocated with dbus_message_allocate_data_slot(). + * + * @param message the message + * @param slot the slot number + * @param data the data to store + * @param free_data_func finalizer function for the data + * @returns #TRUE if there was enough memory to store the data + */ +dbus_bool_t +dbus_message_set_data (DBusMessage *message, + dbus_int32_t slot, + void *data, + DBusFreeFunction free_data_func) +{ + DBusFreeFunction old_free_func; + void *old_data; + dbus_bool_t retval; + + _dbus_return_val_if_fail (message != NULL, FALSE); + _dbus_return_val_if_fail (slot >= 0, FALSE); + + retval = _dbus_data_slot_list_set (&slot_allocator, + &message->slot_list, + slot, data, free_data_func, + &old_free_func, &old_data); + + if (retval) + { + /* Do the actual free outside the message lock */ + if (old_free_func) + (* old_free_func) (old_data); + } + + return retval; +} + +/** + * Retrieves data previously set with dbus_message_set_data(). + * The slot must still be allocated (must not have been freed). + * + * @param message the message + * @param slot the slot to get data from + * @returns the data, or #NULL if not found + */ +void* +dbus_message_get_data (DBusMessage *message, + dbus_int32_t slot) +{ + void *res; + + _dbus_return_val_if_fail (message != NULL, NULL); + + res = _dbus_data_slot_list_get (&slot_allocator, + &message->slot_list, + slot); + + return res; +} + +/** + * Utility function to convert a machine-readable (not translated) + * string into a D-Bus message type. + * + * @code + * "method_call" -> DBUS_MESSAGE_TYPE_METHOD_CALL + * "method_return" -> DBUS_MESSAGE_TYPE_METHOD_RETURN + * "signal" -> DBUS_MESSAGE_TYPE_SIGNAL + * "error" -> DBUS_MESSAGE_TYPE_ERROR + * anything else -> DBUS_MESSAGE_TYPE_INVALID + * @endcode + * + */ +int +dbus_message_type_from_string (const char *type_str) +{ + if (strcmp (type_str, "method_call") == 0) + return DBUS_MESSAGE_TYPE_METHOD_CALL; + if (strcmp (type_str, "method_return") == 0) + return DBUS_MESSAGE_TYPE_METHOD_RETURN; + else if (strcmp (type_str, "signal") == 0) + return DBUS_MESSAGE_TYPE_SIGNAL; + else if (strcmp (type_str, "error") == 0) + return DBUS_MESSAGE_TYPE_ERROR; + else + return DBUS_MESSAGE_TYPE_INVALID; +} + +/** + * Utility function to convert a D-Bus message type into a + * machine-readable string (not translated). + * + * @code + * DBUS_MESSAGE_TYPE_METHOD_CALL -> "method_call" + * DBUS_MESSAGE_TYPE_METHOD_RETURN -> "method_return" + * DBUS_MESSAGE_TYPE_SIGNAL -> "signal" + * DBUS_MESSAGE_TYPE_ERROR -> "error" + * DBUS_MESSAGE_TYPE_INVALID -> "invalid" + * @endcode + * + */ +const char * +dbus_message_type_to_string (int type) +{ + switch (type) + { + case DBUS_MESSAGE_TYPE_METHOD_CALL: + return "method_call"; + case DBUS_MESSAGE_TYPE_METHOD_RETURN: + return "method_return"; + case DBUS_MESSAGE_TYPE_SIGNAL: + return "signal"; + case DBUS_MESSAGE_TYPE_ERROR: + return "error"; + default: + return "invalid"; + } +} + +/** + * Turn a DBusMessage into the marshalled form as described in the D-Bus + * specification. + * + * Generally, this function is only useful for encapsulating D-Bus messages in + * a different protocol. + * + * @param msg the DBusMessage + * @param marshalled_data_p the location to save the marshalled form to + * @param len_p the location to save the length of the marshalled form to + * @returns #FALSE if there was not enough memory + */ +dbus_bool_t +dbus_message_marshal (DBusMessage *msg, + char **marshalled_data_p, + int *len_p) +{ + DBusString tmp; + + _dbus_return_val_if_fail (msg != NULL, FALSE); + _dbus_return_val_if_fail (marshalled_data_p != NULL, FALSE); + _dbus_return_val_if_fail (len_p != NULL, FALSE); + + if (!_dbus_string_init (&tmp)) + return FALSE; + + if (!_dbus_string_copy (&(msg->header.data), 0, &tmp, 0)) + goto fail; + + *len_p = _dbus_string_get_length (&tmp); + + if (!_dbus_string_copy (&(msg->body), 0, &tmp, *len_p)) + goto fail; + + *len_p = _dbus_string_get_length (&tmp); + + if (!_dbus_string_steal_data (&tmp, marshalled_data_p)) + goto fail; + + _dbus_string_free (&tmp); + return TRUE; + + fail: + _dbus_string_free (&tmp); + return FALSE; +} + +/** + * Demarshal a D-Bus message from the format described in the D-Bus + * specification. + * + * Generally, this function is only useful for encapsulating D-Bus messages in + * a different protocol. + * + * @param str the marshalled DBusMessage + * @param len the length of str + * @param error the location to save errors to + * @returns #NULL if there was an error + */ +DBusMessage * +dbus_message_demarshal (const char *str, + int len, + DBusError *error) +{ + DBusMessageLoader *loader; + DBusString *buffer; + DBusMessage *msg; + + _dbus_return_val_if_fail (str != NULL, NULL); + + loader = _dbus_message_loader_new (); + + if (loader == NULL) + return NULL; + + _dbus_message_loader_get_buffer (loader, &buffer); + _dbus_string_append_len (buffer, str, len); + _dbus_message_loader_return_buffer (loader, buffer, len); + + if (!_dbus_message_loader_queue_messages (loader)) + goto fail_oom; + + if (_dbus_message_loader_get_is_corrupted (loader)) + goto fail_corrupt; + + msg = _dbus_message_loader_pop_message (loader); + + if (!msg) + goto fail_oom; + + _dbus_message_loader_unref (loader); + return msg; + + fail_corrupt: + dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, "Message is corrupted"); + _dbus_message_loader_unref (loader); + return NULL; + + fail_oom: + _DBUS_SET_OOM (error); + _dbus_message_loader_unref (loader); + return NULL; +} + +/** + * Returns the number of bytes required to be in the buffer to demarshal a + * D-Bus message. + * + * Generally, this function is only useful for encapsulating D-Bus messages in + * a different protocol. + * + * @param str data to be marshalled + * @param len the length of str + * @param error the location to save errors to + * @returns -1 if there was no valid data to be demarshalled, 0 if there wasn't enough data to determine how much should be demarshalled. Otherwise returns the number of bytes to be demarshalled + * + */ +int +dbus_message_demarshal_bytes_needed(const char *buf, + int len) +{ + DBusString str; + int byte_order, fields_array_len, header_len, body_len; + DBusValidity validity = DBUS_VALID; + int have_message; + + if (!buf || len < DBUS_MINIMUM_HEADER_SIZE) + return 0; + + if (len > DBUS_MAXIMUM_MESSAGE_LENGTH) + len = DBUS_MAXIMUM_MESSAGE_LENGTH; + _dbus_string_init_const_len (&str, buf, len); + + validity = DBUS_VALID; + have_message + = _dbus_header_have_message_untrusted(DBUS_MAXIMUM_MESSAGE_LENGTH, + &validity, &byte_order, + &fields_array_len, + &header_len, + &body_len, + &str, 0, + len); + _dbus_string_free (&str); + + if (validity == DBUS_VALID) + { + _dbus_assert(have_message); + return header_len + body_len; + } + else + { + return -1; /* broken! */ + } +} + +/** @} */ + +/* tests in dbus-message-util.c */ diff --git a/src/dbus/dbus-message.h b/src/dbus/dbus-message.h new file mode 100644 index 0000000..2e29fef --- /dev/null +++ b/src/dbus/dbus-message.h @@ -0,0 +1,233 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-message.h DBusMessage object + * + * Copyright (C) 2002, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION) +#error "Only can be included directly, this file may disappear or change contents." +#endif + +#ifndef DBUS_MESSAGE_H +#define DBUS_MESSAGE_H + +#include +#include +#include +#include +#include +#include + +DBUS_BEGIN_DECLS + +/** + * @addtogroup DBusMessage + * @{ + */ + +typedef struct DBusMessage DBusMessage; +/** Opaque type representing a message iterator. Can be copied by value, and contains no allocated memory so never needs to be freed and can be allocated on the stack. */ +typedef struct DBusMessageIter DBusMessageIter; + +/** + * DBusMessageIter struct; contains no public fields. + */ +struct DBusMessageIter +{ + void *dummy1; /**< Don't use this */ + void *dummy2; /**< Don't use this */ + dbus_uint32_t dummy3; /**< Don't use this */ + int dummy4; /**< Don't use this */ + int dummy5; /**< Don't use this */ + int dummy6; /**< Don't use this */ + int dummy7; /**< Don't use this */ + int dummy8; /**< Don't use this */ + int dummy9; /**< Don't use this */ + int dummy10; /**< Don't use this */ + int dummy11; /**< Don't use this */ + int pad1; /**< Don't use this */ + int pad2; /**< Don't use this */ + void *pad3; /**< Don't use this */ +}; + +DBusMessage* dbus_message_new (int message_type); +DBusMessage* dbus_message_new_method_call (const char *bus_name, + const char *path, + const char *interface, + const char *method); +DBusMessage* dbus_message_new_method_return (DBusMessage *method_call); +DBusMessage* dbus_message_new_signal (const char *path, + const char *interface, + const char *name); +DBusMessage* dbus_message_new_error (DBusMessage *reply_to, + const char *error_name, + const char *error_message); +DBusMessage* dbus_message_new_error_printf (DBusMessage *reply_to, + const char *error_name, + const char *error_format, + ...); + +DBusMessage* dbus_message_copy (const DBusMessage *message); + +DBusMessage* dbus_message_ref (DBusMessage *message); +void dbus_message_unref (DBusMessage *message); +int dbus_message_get_type (DBusMessage *message); +dbus_bool_t dbus_message_set_path (DBusMessage *message, + const char *object_path); +const char* dbus_message_get_path (DBusMessage *message); +dbus_bool_t dbus_message_has_path (DBusMessage *message, + const char *object_path); +dbus_bool_t dbus_message_set_interface (DBusMessage *message, + const char *interface); +const char* dbus_message_get_interface (DBusMessage *message); +dbus_bool_t dbus_message_has_interface (DBusMessage *message, + const char *interface); +dbus_bool_t dbus_message_set_member (DBusMessage *message, + const char *member); +const char* dbus_message_get_member (DBusMessage *message); +dbus_bool_t dbus_message_has_member (DBusMessage *message, + const char *member); +dbus_bool_t dbus_message_set_error_name (DBusMessage *message, + const char *name); +const char* dbus_message_get_error_name (DBusMessage *message); +dbus_bool_t dbus_message_set_destination (DBusMessage *message, + const char *destination); +const char* dbus_message_get_destination (DBusMessage *message); +dbus_bool_t dbus_message_set_sender (DBusMessage *message, + const char *sender); +const char* dbus_message_get_sender (DBusMessage *message); +const char* dbus_message_get_signature (DBusMessage *message); +void dbus_message_set_no_reply (DBusMessage *message, + dbus_bool_t no_reply); +dbus_bool_t dbus_message_get_no_reply (DBusMessage *message); +dbus_bool_t dbus_message_is_method_call (DBusMessage *message, + const char *interface, + const char *method); +dbus_bool_t dbus_message_is_signal (DBusMessage *message, + const char *interface, + const char *signal_name); +dbus_bool_t dbus_message_is_error (DBusMessage *message, + const char *error_name); +dbus_bool_t dbus_message_has_destination (DBusMessage *message, + const char *bus_name); +dbus_bool_t dbus_message_has_sender (DBusMessage *message, + const char *unique_bus_name); +dbus_bool_t dbus_message_has_signature (DBusMessage *message, + const char *signature); +dbus_uint32_t dbus_message_get_serial (DBusMessage *message); +void dbus_message_set_serial (DBusMessage *message, + dbus_uint32_t serial); +dbus_bool_t dbus_message_set_reply_serial (DBusMessage *message, + dbus_uint32_t reply_serial); +dbus_uint32_t dbus_message_get_reply_serial (DBusMessage *message); + +void dbus_message_set_auto_start (DBusMessage *message, + dbus_bool_t auto_start); +dbus_bool_t dbus_message_get_auto_start (DBusMessage *message); + +dbus_bool_t dbus_message_get_path_decomposed (DBusMessage *message, + char ***path); + +dbus_bool_t dbus_message_append_args (DBusMessage *message, + int first_arg_type, + ...); +dbus_bool_t dbus_message_append_args_valist (DBusMessage *message, + int first_arg_type, + va_list var_args); +dbus_bool_t dbus_message_get_args (DBusMessage *message, + DBusError *error, + int first_arg_type, + ...); +dbus_bool_t dbus_message_get_args_valist (DBusMessage *message, + DBusError *error, + int first_arg_type, + va_list var_args); + + +dbus_bool_t dbus_message_iter_init (DBusMessage *message, + DBusMessageIter *iter); +dbus_bool_t dbus_message_iter_has_next (DBusMessageIter *iter); +dbus_bool_t dbus_message_iter_next (DBusMessageIter *iter); +char* dbus_message_iter_get_signature (DBusMessageIter *iter); +int dbus_message_iter_get_arg_type (DBusMessageIter *iter); +int dbus_message_iter_get_element_type (DBusMessageIter *iter); +void dbus_message_iter_recurse (DBusMessageIter *iter, + DBusMessageIter *sub); +void dbus_message_iter_get_basic (DBusMessageIter *iter, + void *value); +#ifndef DBUS_DISABLE_DEPRECATED +/* This function returns the wire protocol size of the array in bytes, + * you do not want to know that probably + */ +DBUS_DEPRECATED int dbus_message_iter_get_array_len (DBusMessageIter *iter); +#endif +void dbus_message_iter_get_fixed_array (DBusMessageIter *iter, + void *value, + int *n_elements); + + +void dbus_message_iter_init_append (DBusMessage *message, + DBusMessageIter *iter); +dbus_bool_t dbus_message_iter_append_basic (DBusMessageIter *iter, + int type, + const void *value); +dbus_bool_t dbus_message_iter_append_fixed_array (DBusMessageIter *iter, + int element_type, + const void *value, + int n_elements); +dbus_bool_t dbus_message_iter_open_container (DBusMessageIter *iter, + int type, + const char *contained_signature, + DBusMessageIter *sub); +dbus_bool_t dbus_message_iter_close_container (DBusMessageIter *iter, + DBusMessageIter *sub); + +void dbus_message_lock (DBusMessage *message); + +dbus_bool_t dbus_set_error_from_message (DBusError *error, + DBusMessage *message); + + +dbus_bool_t dbus_message_allocate_data_slot (dbus_int32_t *slot_p); +void dbus_message_free_data_slot (dbus_int32_t *slot_p); +dbus_bool_t dbus_message_set_data (DBusMessage *message, + dbus_int32_t slot, + void *data, + DBusFreeFunction free_data_func); +void* dbus_message_get_data (DBusMessage *message, + dbus_int32_t slot); + +int dbus_message_type_from_string (const char *type_str); +const char* dbus_message_type_to_string (int type); + +dbus_bool_t dbus_message_marshal (DBusMessage *msg, + char **marshalled_data_p, + int *len_p); +DBusMessage* dbus_message_demarshal (const char *str, + int len, + DBusError *error); + +int dbus_message_demarshal_bytes_needed (const char *str, + int len); + +/** @} */ + +DBUS_END_DECLS + +#endif /* DBUS_MESSAGE_H */ diff --git a/src/dbus/dbus-misc.c b/src/dbus/dbus-misc.c new file mode 100644 index 0000000..758e1a0 --- /dev/null +++ b/src/dbus/dbus-misc.c @@ -0,0 +1,248 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-misc.c A few assorted public functions that don't fit elsewhere + * + * Copyright (C) 2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include "dbus-misc.h" +#include "dbus-internals.h" +#include "dbus-string.h" + +/** + * @defgroup DBusMisc Miscellaneous + * @ingroup DBus + * @brief Miscellaneous API that doesn't cleanly fit anywhere else + * + * @{ + */ + +/** + * Obtains the machine UUID of the machine this process is running on. + * + * The returned string must be freed with dbus_free(). + * + * This UUID is guaranteed to remain the same until the next reboot + * (unless the sysadmin foolishly changes it and screws themselves). + * It will usually remain the same across reboots also, but hardware + * configuration changes or rebuilding the machine could break that. + * + * The idea is that two processes with the same machine ID should be + * able to use shared memory, UNIX domain sockets, process IDs, and other + * features of the OS that require both processes to be running + * on the same OS kernel instance. + * + * The machine ID can also be used to create unique per-machine + * instances. For example, you could use it in bus names or + * X selection names. + * + * The machine ID is preferred over the machine hostname, because + * the hostname is frequently set to "localhost.localdomain" and + * may also change at runtime. + * + * You can get the machine ID of a remote application by invoking the + * method GetMachineId from interface org.freedesktop.DBus.Peer. + * + * If the remote application has the same machine ID as the one + * returned by this function, then the remote application is on the + * same machine as your application. + * + * The UUID is not a UUID in the sense of RFC4122; the details + * are explained in the D-Bus specification. + * + * @returns a 32-byte-long hex-encoded UUID string, or #NULL if insufficient memory + */ +char* +dbus_get_local_machine_id (void) +{ + DBusString uuid; + char *s; + + s = NULL; + + if (!_dbus_string_init (&uuid)) + return NULL; + + if (!_dbus_get_local_machine_uuid_encoded (&uuid) || + !_dbus_string_steal_data (&uuid, &s)) + { + _dbus_string_free (&uuid); + return NULL; + } + else + { + _dbus_string_free (&uuid); + return s; + } + +} + +/** + * @def DBUS_MAJOR_VERSION + * + * The COMPILE TIME major version of libdbus, that is, the "X" in "X.Y.Z", + * as an integer literal. Consider carefully whether to use this or the + * runtime version from dbus_get_version(). + */ + +/** + * @def DBUS_MINOR_VERSION + * + * The COMPILE TIME minor version of libdbus, that is, the "Y" in "X.Y.Z", + * as an integer literal. Consider carefully whether to use this or the + * runtime version from dbus_get_version(). + */ + +/** + * @def DBUS_MICRO_VERSION + * + * The COMPILE TIME micro version of libdbus, that is, the "Z" in "X.Y.Z", + * as an integer literal. Consider carefully whether to use this or the + * runtime version from dbus_get_version(). + */ + +/** + * @def DBUS_VERSION + * + * The COMPILE TIME version of libdbus, as a single integer that has 0 in the most + * significant byte, the major version in the next most significant byte, + * the minor version in the third most significant, and the micro version in the + * least significant byte. This means two DBUS_VERSION can be compared to see + * which is higher. + * + * Consider carefully whether to use this or the runtime version from + * dbus_get_version(). + */ + +/** + * @def DBUS_VERSION_STRING + * + * The COMPILE TIME version of libdbus, as a string "X.Y.Z". + * + * Consider carefully whether to use this or the runtime version from + * dbus_get_version(). + */ + +/** + * Gets the DYNAMICALLY LINKED version of libdbus. Alternatively, there + * are macros #DBUS_MAJOR_VERSION, #DBUS_MINOR_VERSION, #DBUS_MICRO_VERSION, + * and #DBUS_VERSION which allow you to test the VERSION YOU ARE COMPILED AGAINST. + * In other words, you can get either the runtime or the compile-time version. + * Think carefully about which of these you want in a given case. + * + * The libdbus full version number is "MAJOR.MINOR.MICRO" where the + * MINOR changes if API is added, and the MICRO changes with each + * release of a MAJOR.MINOR series. The MINOR is an odd number for + * development releases and an even number for stable releases. + * + * @param major_version_p pointer to return the major version, or #NULL + * @param minor_version_p pointer to return the minor version, or #NULL + * @param micro_version_p pointer to return the micro version, or #NULL + * + */ +void +dbus_get_version (int *major_version_p, + int *minor_version_p, + int *micro_version_p) +{ + if (major_version_p) + *major_version_p = DBUS_MAJOR_VERSION; + if (minor_version_p) + *minor_version_p = DBUS_MINOR_VERSION; + if (micro_version_p) + *micro_version_p = DBUS_MICRO_VERSION; +} + + +/** @} */ /* End of public API */ + +#ifdef DBUS_BUILD_TESTS + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +#include "dbus-test.h" +#include + + +dbus_bool_t +_dbus_misc_test (void) +{ + int major, minor, micro; + DBusString str; + + /* make sure we don't crash on NULL */ + dbus_get_version (NULL, NULL, NULL); + + /* Now verify that all the compile-time version stuff + * is right and matches the runtime. These tests + * are mostly intended to catch various kinds of + * typo (mixing up major and minor, that sort of thing). + */ + dbus_get_version (&major, &minor, µ); + + _dbus_assert (major == DBUS_MAJOR_VERSION); + _dbus_assert (minor == DBUS_MINOR_VERSION); + _dbus_assert (micro == DBUS_MICRO_VERSION); + +#define MAKE_VERSION(x, y, z) (((x) << 16) | ((y) << 8) | (z)) + + /* check that MAKE_VERSION works and produces the intended ordering */ + _dbus_assert (MAKE_VERSION (1, 0, 0) > MAKE_VERSION (0, 0, 0)); + _dbus_assert (MAKE_VERSION (1, 1, 0) > MAKE_VERSION (1, 0, 0)); + _dbus_assert (MAKE_VERSION (1, 1, 1) > MAKE_VERSION (1, 1, 0)); + + _dbus_assert (MAKE_VERSION (2, 0, 0) > MAKE_VERSION (1, 1, 1)); + _dbus_assert (MAKE_VERSION (2, 1, 0) > MAKE_VERSION (1, 1, 1)); + _dbus_assert (MAKE_VERSION (2, 1, 1) > MAKE_VERSION (1, 1, 1)); + + /* check DBUS_VERSION */ + _dbus_assert (MAKE_VERSION (major, minor, micro) == DBUS_VERSION); + + /* check that ordering works with DBUS_VERSION */ + _dbus_assert (MAKE_VERSION (major - 1, minor, micro) < DBUS_VERSION); + _dbus_assert (MAKE_VERSION (major, minor - 1, micro) < DBUS_VERSION); + _dbus_assert (MAKE_VERSION (major, minor, micro - 1) < DBUS_VERSION); + + _dbus_assert (MAKE_VERSION (major + 1, minor, micro) > DBUS_VERSION); + _dbus_assert (MAKE_VERSION (major, minor + 1, micro) > DBUS_VERSION); + _dbus_assert (MAKE_VERSION (major, minor, micro + 1) > DBUS_VERSION); + + /* Check DBUS_VERSION_STRING */ + + if (!_dbus_string_init (&str)) + _dbus_assert_not_reached ("no memory"); + + if (!(_dbus_string_append_int (&str, major) && + _dbus_string_append_byte (&str, '.') && + _dbus_string_append_int (&str, minor) && + _dbus_string_append_byte (&str, '.') && + _dbus_string_append_int (&str, micro))) + _dbus_assert_not_reached ("no memory"); + + _dbus_assert (_dbus_string_equal_c_str (&str, DBUS_VERSION_STRING)); + + _dbus_string_free (&str); + + return TRUE; +} + +#endif /* !DOXYGEN_SHOULD_SKIP_THIS */ + +#endif diff --git a/src/dbus/dbus-misc.h b/src/dbus/dbus-misc.h new file mode 100644 index 0000000..c59ce70 --- /dev/null +++ b/src/dbus/dbus-misc.h @@ -0,0 +1,51 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-misc.h A few assorted public functions that don't fit elsewhere + * + * Copyright (C) 2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION) +#error "Only can be included directly, this file may disappear or change contents." +#endif + +#ifndef DBUS_MISC_H +#define DBUS_MISC_H + +#include +#include + +DBUS_BEGIN_DECLS + +/** + * @addtogroup DBusMisc + * @{ + */ + +char* dbus_get_local_machine_id (void); + +void dbus_get_version (int *major_version_p, + int *minor_version_p, + int *micro_version_p); + +/** @} */ + +DBUS_END_DECLS + +#endif /* DBUS_MISC_H */ + diff --git a/src/dbus/dbus-object-tree.c b/src/dbus/dbus-object-tree.c new file mode 100644 index 0000000..953aa3b --- /dev/null +++ b/src/dbus/dbus-object-tree.c @@ -0,0 +1,1949 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-object-tree.c DBusObjectTree (internals of DBusConnection) + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include "dbus-object-tree.h" +#include "dbus-connection-internal.h" +#include "dbus-internals.h" +#include "dbus-hash.h" +#include "dbus-protocol.h" +#include "dbus-string.h" +#include +#include + +/** + * @defgroup DBusObjectTree A hierarchy of objects with container-contained relationship + * @ingroup DBusInternals + * @brief DBusObjectTree is used by DBusConnection to track the object tree + * + * Types and functions related to DBusObjectTree. These + * are all library-internal. + * + * @{ + */ + +/** Subnode of the object hierarchy */ +typedef struct DBusObjectSubtree DBusObjectSubtree; + +static DBusObjectSubtree* _dbus_object_subtree_new (const char *name, + const DBusObjectPathVTable *vtable, + void *user_data); +static DBusObjectSubtree* _dbus_object_subtree_ref (DBusObjectSubtree *subtree); +static void _dbus_object_subtree_unref (DBusObjectSubtree *subtree); + +/** + * Internals of DBusObjectTree + */ +struct DBusObjectTree +{ + int refcount; /**< Reference count */ + DBusConnection *connection; /**< Connection this tree belongs to */ + + DBusObjectSubtree *root; /**< Root of the tree ("/" node) */ +}; + +/** + * Struct representing a single registered subtree handler, or node + * that's a parent of a registered subtree handler. If + * message_function != NULL there's actually a handler at this node. + */ +struct DBusObjectSubtree +{ + DBusAtomic refcount; /**< Reference count */ + DBusObjectSubtree *parent; /**< Parent node */ + DBusObjectPathUnregisterFunction unregister_function; /**< Function to call on unregister */ + DBusObjectPathMessageFunction message_function; /**< Function to handle messages */ + void *user_data; /**< Data for functions */ + DBusObjectSubtree **subtrees; /**< Child nodes */ + int n_subtrees; /**< Number of child nodes */ + int max_subtrees; /**< Number of allocated entries in subtrees */ + unsigned int invoke_as_fallback : 1; /**< Whether to invoke message_function when child nodes don't handle the message */ + char name[1]; /**< Allocated as large as necessary */ +}; + +/** + * Creates a new object tree, representing a mapping from paths + * to handler vtables. + * + * @param connection the connection this tree belongs to + * @returns the new tree or #NULL if no memory + */ +DBusObjectTree* +_dbus_object_tree_new (DBusConnection *connection) +{ + DBusObjectTree *tree; + + /* the connection passed in here isn't fully constructed, + * so don't do anything more than store a pointer to + * it + */ + + tree = dbus_new0 (DBusObjectTree, 1); + if (tree == NULL) + goto oom; + + tree->refcount = 1; + tree->connection = connection; + tree->root = _dbus_object_subtree_new ("/", NULL, NULL); + if (tree->root == NULL) + goto oom; + tree->root->invoke_as_fallback = TRUE; + + return tree; + + oom: + if (tree) + { + dbus_free (tree); + } + + return NULL; +} + +/** + * Increment the reference count + * @param tree the object tree + * @returns the object tree + */ +DBusObjectTree * +_dbus_object_tree_ref (DBusObjectTree *tree) +{ + _dbus_assert (tree->refcount > 0); + + tree->refcount += 1; + + return tree; +} + +/** + * Decrement the reference count + * @param tree the object tree + */ +void +_dbus_object_tree_unref (DBusObjectTree *tree) +{ + _dbus_assert (tree->refcount > 0); + + tree->refcount -= 1; + + if (tree->refcount == 0) + { + _dbus_object_tree_free_all_unlocked (tree); + + dbus_free (tree); + } +} + +/** Set to 1 to get a bunch of debug spew about finding the + * subtree nodes + */ +#define VERBOSE_FIND 0 + +static DBusObjectSubtree* +find_subtree_recurse (DBusObjectSubtree *subtree, + const char **path, + dbus_bool_t create_if_not_found, + int *index_in_parent, + dbus_bool_t *exact_match) +{ + int i, j; + dbus_bool_t return_deepest_match; + + return_deepest_match = exact_match != NULL; + + _dbus_assert (!(return_deepest_match && create_if_not_found)); + + if (path[0] == NULL) + { +#if VERBOSE_FIND + _dbus_verbose (" path exhausted, returning %s\n", + subtree->name); +#endif + if (exact_match != NULL) + *exact_match = TRUE; + return subtree; + } + +#if VERBOSE_FIND + _dbus_verbose (" searching children of %s for %s\n", + subtree->name, path[0]); +#endif + + i = 0; + j = subtree->n_subtrees; + while (i < j) + { + int k, v; + + k = (i + j) / 2; + v = strcmp (path[0], subtree->subtrees[k]->name); + +#if VERBOSE_FIND + _dbus_verbose (" %s cmp %s = %d\n", + path[0], subtree->subtrees[k]->name, + v); +#endif + + if (v == 0) + { + if (index_in_parent) + { +#if VERBOSE_FIND + _dbus_verbose (" storing parent index %d\n", k); +#endif + *index_in_parent = k; + } + + if (return_deepest_match) + { + DBusObjectSubtree *next; + + next = find_subtree_recurse (subtree->subtrees[k], + &path[1], create_if_not_found, + index_in_parent, exact_match); + if (next == NULL && + subtree->invoke_as_fallback) + { +#if VERBOSE_FIND + _dbus_verbose (" no deeper match found, returning %s\n", + subtree->name); +#endif + if (exact_match != NULL) + *exact_match = FALSE; + return subtree; + } + else + return next; + } + else + return find_subtree_recurse (subtree->subtrees[k], + &path[1], create_if_not_found, + index_in_parent, exact_match); + } + else if (v < 0) + { + j = k; + } + else + { + i = k + 1; + } + } + +#if VERBOSE_FIND + _dbus_verbose (" no match found, current tree %s, create_if_not_found = %d\n", + subtree->name, create_if_not_found); +#endif + + if (create_if_not_found) + { + DBusObjectSubtree* child; + int child_pos, new_n_subtrees; + +#if VERBOSE_FIND + _dbus_verbose (" creating subtree %s\n", + path[0]); +#endif + + child = _dbus_object_subtree_new (path[0], + NULL, NULL); + if (child == NULL) + return NULL; + + new_n_subtrees = subtree->n_subtrees + 1; + if (new_n_subtrees > subtree->max_subtrees) + { + int new_max_subtrees; + DBusObjectSubtree **new_subtrees; + + new_max_subtrees = subtree->max_subtrees == 0 ? 1 : 2 * subtree->max_subtrees; + new_subtrees = dbus_realloc (subtree->subtrees, + new_max_subtrees * sizeof (DBusObjectSubtree*)); + if (new_subtrees == NULL) + { + _dbus_object_subtree_unref (child); + return NULL; + } + subtree->subtrees = new_subtrees; + subtree->max_subtrees = new_max_subtrees; + } + + /* The binary search failed, so i == j points to the + place the child should be inserted. */ + child_pos = i; + _dbus_assert (child_pos < new_n_subtrees && + new_n_subtrees <= subtree->max_subtrees); + if (child_pos + 1 < new_n_subtrees) + { + memmove (&subtree->subtrees[child_pos+1], + &subtree->subtrees[child_pos], + (new_n_subtrees - child_pos - 1) * + sizeof subtree->subtrees[0]); + } + subtree->subtrees[child_pos] = child; + + if (index_in_parent) + *index_in_parent = child_pos; + subtree->n_subtrees = new_n_subtrees; + child->parent = subtree; + + return find_subtree_recurse (child, + &path[1], create_if_not_found, + index_in_parent, exact_match); + } + else + { + if (exact_match != NULL) + *exact_match = FALSE; + return (return_deepest_match && subtree->invoke_as_fallback) ? subtree : NULL; + } +} + +static DBusObjectSubtree* +find_subtree (DBusObjectTree *tree, + const char **path, + int *index_in_parent) +{ + DBusObjectSubtree *subtree; + +#if VERBOSE_FIND + _dbus_verbose ("Looking for exact registered subtree\n"); +#endif + + subtree = find_subtree_recurse (tree->root, path, FALSE, index_in_parent, NULL); + + if (subtree && subtree->message_function == NULL) + return NULL; + else + return subtree; +} + +static DBusObjectSubtree* +lookup_subtree (DBusObjectTree *tree, + const char **path) +{ +#if VERBOSE_FIND + _dbus_verbose ("Looking for subtree\n"); +#endif + return find_subtree_recurse (tree->root, path, FALSE, NULL, NULL); +} + +static DBusObjectSubtree* +find_handler (DBusObjectTree *tree, + const char **path, + dbus_bool_t *exact_match) +{ +#if VERBOSE_FIND + _dbus_verbose ("Looking for deepest handler\n"); +#endif + _dbus_assert (exact_match != NULL); + + *exact_match = FALSE; /* ensure always initialized */ + + return find_subtree_recurse (tree->root, path, FALSE, NULL, exact_match); +} + +static DBusObjectSubtree* +ensure_subtree (DBusObjectTree *tree, + const char **path) +{ +#if VERBOSE_FIND + _dbus_verbose ("Ensuring subtree\n"); +#endif + return find_subtree_recurse (tree->root, path, TRUE, NULL, NULL); +} + +static char *flatten_path (const char **path); + +/** + * Registers a new subtree in the global object tree. + * + * @param tree the global object tree + * @param fallback #TRUE to handle messages to children of this path + * @param path NULL-terminated array of path elements giving path to subtree + * @param vtable the vtable used to traverse this subtree + * @param user_data user data to pass to methods in the vtable + * @param error address where an error can be returned + * @returns #FALSE if an error (#DBUS_ERROR_NO_MEMORY or + * #DBUS_ERROR_OBJECT_PATH_IN_USE) is reported + */ +dbus_bool_t +_dbus_object_tree_register (DBusObjectTree *tree, + dbus_bool_t fallback, + const char **path, + const DBusObjectPathVTable *vtable, + void *user_data, + DBusError *error) +{ + DBusObjectSubtree *subtree; + + _dbus_assert (tree != NULL); + _dbus_assert (vtable->message_function != NULL); + _dbus_assert (path != NULL); + + subtree = ensure_subtree (tree, path); + if (subtree == NULL) + { + _DBUS_SET_OOM (error); + return FALSE; + } + + if (subtree->message_function != NULL) + { + if (error != NULL) + { + char *complete_path = flatten_path (path); + + dbus_set_error (error, DBUS_ERROR_OBJECT_PATH_IN_USE, + "A handler is already registered for %s", + complete_path ? complete_path + : "(cannot represent path: out of memory!)"); + + dbus_free (complete_path); + } + + return FALSE; + } + + subtree->message_function = vtable->message_function; + subtree->unregister_function = vtable->unregister_function; + subtree->user_data = user_data; + subtree->invoke_as_fallback = fallback != FALSE; + + return TRUE; +} + +/** + * Unregisters an object subtree that was registered with the + * same path. + * + * @param tree the global object tree + * @param path path to the subtree (same as the one passed to _dbus_object_tree_register()) + */ +void +_dbus_object_tree_unregister_and_unlock (DBusObjectTree *tree, + const char **path) +{ + int i; + DBusObjectSubtree *subtree; + DBusObjectPathUnregisterFunction unregister_function; + void *user_data; + DBusConnection *connection; + + _dbus_assert (path != NULL); + + unregister_function = NULL; + user_data = NULL; + + subtree = find_subtree (tree, path, &i); + +#ifndef DBUS_DISABLE_CHECKS + if (subtree == NULL) + { + _dbus_warn ("Attempted to unregister path (path[0] = %s path[1] = %s) which isn't registered\n", + path[0] ? path[0] : "null", + path[1] ? path[1] : "null"); + goto unlock; + } +#else + _dbus_assert (subtree != NULL); +#endif + + _dbus_assert (subtree->parent == NULL || + (i >= 0 && subtree->parent->subtrees[i] == subtree)); + + subtree->message_function = NULL; + + unregister_function = subtree->unregister_function; + user_data = subtree->user_data; + + subtree->unregister_function = NULL; + subtree->user_data = NULL; + + /* If we have no subtrees of our own, remove from + * our parent (FIXME could also be more aggressive + * and remove our parent if it becomes empty) + */ + if (subtree->parent && subtree->n_subtrees == 0) + { + /* assumes a 0-byte memmove is OK */ + memmove (&subtree->parent->subtrees[i], + &subtree->parent->subtrees[i+1], + (subtree->parent->n_subtrees - i - 1) * + sizeof (subtree->parent->subtrees[0])); + subtree->parent->n_subtrees -= 1; + + subtree->parent = NULL; + + _dbus_object_subtree_unref (subtree); + } + subtree = NULL; + +unlock: + connection = tree->connection; + + /* Unlock and call application code */ +#ifdef DBUS_BUILD_TESTS + if (connection) +#endif + { + _dbus_connection_ref_unlocked (connection); + _dbus_verbose ("unlock %s\n", _DBUS_FUNCTION_NAME); + _dbus_connection_unlock (connection); + } + + if (unregister_function) + (* unregister_function) (connection, user_data); + +#ifdef DBUS_BUILD_TESTS + if (connection) +#endif + dbus_connection_unref (connection); +} + +static void +free_subtree_recurse (DBusConnection *connection, + DBusObjectSubtree *subtree) +{ + /* Delete them from the end, for slightly + * more robustness against odd reentrancy. + */ + while (subtree->n_subtrees > 0) + { + DBusObjectSubtree *child; + + child = subtree->subtrees[subtree->n_subtrees - 1]; + subtree->subtrees[subtree->n_subtrees - 1] = NULL; + subtree->n_subtrees -= 1; + child->parent = NULL; + + free_subtree_recurse (connection, child); + } + + /* Call application code */ + if (subtree->unregister_function) + (* subtree->unregister_function) (connection, + subtree->user_data); + + subtree->message_function = NULL; + subtree->unregister_function = NULL; + subtree->user_data = NULL; + + /* Now free ourselves */ + _dbus_object_subtree_unref (subtree); +} + +/** + * Free all the handlers in the tree. Lock on tree's connection + * must not be held. + * + * @param tree the object tree + */ +void +_dbus_object_tree_free_all_unlocked (DBusObjectTree *tree) +{ + if (tree->root) + free_subtree_recurse (tree->connection, + tree->root); + tree->root = NULL; +} + +static dbus_bool_t +_dbus_object_tree_list_registered_unlocked (DBusObjectTree *tree, + const char **parent_path, + char ***child_entries) +{ + DBusObjectSubtree *subtree; + char **retval; + + _dbus_assert (parent_path != NULL); + _dbus_assert (child_entries != NULL); + + *child_entries = NULL; + + subtree = lookup_subtree (tree, parent_path); + if (subtree == NULL) + { + retval = dbus_new0 (char *, 1); + } + else + { + int i; + retval = dbus_new0 (char*, subtree->n_subtrees + 1); + if (retval == NULL) + goto out; + i = 0; + while (i < subtree->n_subtrees) + { + retval[i] = _dbus_strdup (subtree->subtrees[i]->name); + if (retval[i] == NULL) + { + dbus_free_string_array (retval); + retval = NULL; + goto out; + } + ++i; + } + } + + out: + + *child_entries = retval; + return retval != NULL; +} + +static DBusHandlerResult +handle_default_introspect_and_unlock (DBusObjectTree *tree, + DBusMessage *message, + const char **path) +{ + DBusString xml; + DBusHandlerResult result; + char **children; + int i; + DBusMessage *reply; + DBusMessageIter iter; + const char *v_STRING; + dbus_bool_t already_unlocked; + + /* We have the connection lock here */ + + already_unlocked = FALSE; + + _dbus_verbose (" considering default Introspect() handler...\n"); + + reply = NULL; + + if (!dbus_message_is_method_call (message, + DBUS_INTERFACE_INTROSPECTABLE, + "Introspect")) + { +#ifdef DBUS_BUILD_TESTS + if (tree->connection) +#endif + { + _dbus_verbose ("unlock %s %d\n", _DBUS_FUNCTION_NAME, __LINE__); + _dbus_connection_unlock (tree->connection); + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + _dbus_verbose (" using default Introspect() handler!\n"); + + if (!_dbus_string_init (&xml)) + { +#ifdef DBUS_BUILD_TESTS + if (tree->connection) +#endif + { + _dbus_verbose ("unlock %s %d\n", _DBUS_FUNCTION_NAME, __LINE__); + _dbus_connection_unlock (tree->connection); + } + + return DBUS_HANDLER_RESULT_NEED_MEMORY; + } + + result = DBUS_HANDLER_RESULT_NEED_MEMORY; + + children = NULL; + if (!_dbus_object_tree_list_registered_unlocked (tree, path, &children)) + goto out; + + if (!_dbus_string_append (&xml, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE)) + goto out; + + if (!_dbus_string_append (&xml, "\n")) + goto out; + + i = 0; + while (children[i] != NULL) + { + if (!_dbus_string_append_printf (&xml, " \n", + children[i])) + goto out; + + ++i; + } + + if (!_dbus_string_append (&xml, "\n")) + goto out; + + reply = dbus_message_new_method_return (message); + if (reply == NULL) + goto out; + + dbus_message_iter_init_append (reply, &iter); + v_STRING = _dbus_string_get_const_data (&xml); + if (!dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &v_STRING)) + goto out; + +#ifdef DBUS_BUILD_TESTS + if (tree->connection) +#endif + { + already_unlocked = TRUE; + + if (!_dbus_connection_send_and_unlock (tree->connection, reply, NULL)) + goto out; + } + + result = DBUS_HANDLER_RESULT_HANDLED; + + out: +#ifdef DBUS_BUILD_TESTS + if (tree->connection) +#endif + { + if (!already_unlocked) + { + _dbus_verbose ("unlock %s %d\n", _DBUS_FUNCTION_NAME, __LINE__); + _dbus_connection_unlock (tree->connection); + } + } + + _dbus_string_free (&xml); + dbus_free_string_array (children); + if (reply) + dbus_message_unref (reply); + + return result; +} + +/** + * Tries to dispatch a message by directing it to handler for the + * object path listed in the message header, if any. Messages are + * dispatched first to the registered handler that matches the largest + * number of path elements; that is, message to /foo/bar/baz would go + * to the handler for /foo/bar before the one for /foo. + * + * @todo thread problems + * + * @param tree the global object tree + * @param message the message to dispatch + * @returns whether message was handled successfully + */ +DBusHandlerResult +_dbus_object_tree_dispatch_and_unlock (DBusObjectTree *tree, + DBusMessage *message) +{ + char **path; + dbus_bool_t exact_match; + DBusList *list; + DBusList *link; + DBusHandlerResult result; + DBusObjectSubtree *subtree; + +#if 0 + _dbus_verbose ("Dispatch of message by object path\n"); +#endif + + path = NULL; + if (!dbus_message_get_path_decomposed (message, &path)) + { +#ifdef DBUS_BUILD_TESTS + if (tree->connection) +#endif + { + _dbus_verbose ("unlock %s\n", _DBUS_FUNCTION_NAME); + _dbus_connection_unlock (tree->connection); + } + + _dbus_verbose ("No memory to get decomposed path\n"); + + return DBUS_HANDLER_RESULT_NEED_MEMORY; + } + + if (path == NULL) + { +#ifdef DBUS_BUILD_TESTS + if (tree->connection) +#endif + { + _dbus_verbose ("unlock %s\n", _DBUS_FUNCTION_NAME); + _dbus_connection_unlock (tree->connection); + } + + _dbus_verbose ("No path field in message\n"); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + /* Find the deepest path that covers the path in the message */ + subtree = find_handler (tree, (const char**) path, &exact_match); + + /* Build a list of all paths that cover the path in the message */ + + list = NULL; + + while (subtree != NULL) + { + if (subtree->message_function != NULL && (exact_match || subtree->invoke_as_fallback)) + { + _dbus_object_subtree_ref (subtree); + + /* run deepest paths first */ + if (!_dbus_list_append (&list, subtree)) + { + result = DBUS_HANDLER_RESULT_NEED_MEMORY; + _dbus_object_subtree_unref (subtree); + goto free_and_return; + } + } + + exact_match = FALSE; + subtree = subtree->parent; + } + + _dbus_verbose ("%d handlers in the path tree for this message\n", + _dbus_list_get_length (&list)); + + /* Invoke each handler in the list */ + + result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + link = _dbus_list_get_first_link (&list); + while (link != NULL) + { + DBusList *next = _dbus_list_get_next_link (&list, link); + subtree = link->data; + + /* message_function is NULL if we're unregistered + * due to reentrancy + */ + if (subtree->message_function) + { + DBusObjectPathMessageFunction message_function; + void *user_data; + + message_function = subtree->message_function; + user_data = subtree->user_data; + +#if 0 + _dbus_verbose (" (invoking a handler)\n"); +#endif + +#ifdef DBUS_BUILD_TESTS + if (tree->connection) +#endif + { + _dbus_verbose ("unlock %s\n", _DBUS_FUNCTION_NAME); + _dbus_connection_unlock (tree->connection); + } + + /* FIXME you could unregister the subtree in another thread + * before we invoke the callback, and I can't figure out a + * good way to solve this. + */ + + result = (* message_function) (tree->connection, + message, + user_data); + +#ifdef DBUS_BUILD_TESTS + if (tree->connection) +#endif + _dbus_connection_lock (tree->connection); + + if (result != DBUS_HANDLER_RESULT_NOT_YET_HANDLED) + goto free_and_return; + } + + link = next; + } + + free_and_return: + + if (result == DBUS_HANDLER_RESULT_NOT_YET_HANDLED) + { + /* This hardcoded default handler does a minimal Introspect() + */ + result = handle_default_introspect_and_unlock (tree, message, + (const char**) path); + } + else + { +#ifdef DBUS_BUILD_TESTS + if (tree->connection) +#endif + { + _dbus_verbose ("unlock %s\n", _DBUS_FUNCTION_NAME); + _dbus_connection_unlock (tree->connection); + } + } + + while (list != NULL) + { + link = _dbus_list_get_first_link (&list); + _dbus_object_subtree_unref (link->data); + _dbus_list_remove_link (&list, link); + } + + dbus_free_string_array (path); + + return result; +} + +/** + * Looks up the data passed to _dbus_object_tree_register() for a + * handler at the given path. + * + * @param tree the global object tree + * @param path NULL-terminated array of path elements giving path to subtree + * @returns the object's user_data or #NULL if none found + */ +void* +_dbus_object_tree_get_user_data_unlocked (DBusObjectTree *tree, + const char **path) +{ + dbus_bool_t exact_match; + DBusObjectSubtree *subtree; + + _dbus_assert (tree != NULL); + _dbus_assert (path != NULL); + + /* Find the deepest path that covers the path in the message */ + subtree = find_handler (tree, (const char**) path, &exact_match); + + if ((subtree == NULL) || !exact_match) + { + _dbus_verbose ("%s: No object at specified path found\n", + _DBUS_FUNCTION_NAME); + return NULL; + } + + return subtree->user_data; +} + +/** + * Allocates a subtree object. + * + * @param name name to duplicate. + * @returns newly-allocated subtree + */ +static DBusObjectSubtree* +allocate_subtree_object (const char *name) +{ + int len; + DBusObjectSubtree *subtree; + const size_t front_padding = _DBUS_STRUCT_OFFSET (DBusObjectSubtree, name); + + _dbus_assert (name != NULL); + + len = strlen (name); + + subtree = dbus_malloc (MAX (front_padding + (len + 1), sizeof (DBusObjectSubtree))); + + if (subtree == NULL) + return NULL; + + memcpy (subtree->name, name, len + 1); + + return subtree; +} + +static DBusObjectSubtree* +_dbus_object_subtree_new (const char *name, + const DBusObjectPathVTable *vtable, + void *user_data) +{ + DBusObjectSubtree *subtree; + + subtree = allocate_subtree_object (name); + if (subtree == NULL) + goto oom; + + _dbus_assert (name != NULL); + + subtree->parent = NULL; + + if (vtable) + { + subtree->message_function = vtable->message_function; + subtree->unregister_function = vtable->unregister_function; + } + else + { + subtree->message_function = NULL; + subtree->unregister_function = NULL; + } + + subtree->user_data = user_data; + subtree->refcount.value = 1; + subtree->subtrees = NULL; + subtree->n_subtrees = 0; + subtree->max_subtrees = 0; + subtree->invoke_as_fallback = FALSE; + + return subtree; + + oom: + return NULL; +} + +static DBusObjectSubtree * +_dbus_object_subtree_ref (DBusObjectSubtree *subtree) +{ + _dbus_assert (subtree->refcount.value > 0); + _dbus_atomic_inc (&subtree->refcount); + + return subtree; +} + +static void +_dbus_object_subtree_unref (DBusObjectSubtree *subtree) +{ + _dbus_assert (subtree->refcount.value > 0); + + if (_dbus_atomic_dec (&subtree->refcount) == 1) + { + _dbus_assert (subtree->unregister_function == NULL); + _dbus_assert (subtree->message_function == NULL); + + dbus_free (subtree->subtrees); + dbus_free (subtree); + } +} + +/** + * Lists the registered fallback handlers and object path handlers at + * the given parent_path. The returned array should be freed with + * dbus_free_string_array(). + * + * @param tree the object tree + * @param parent_path the path to list the child handlers of + * @param child_entries returns #NULL-terminated array of children + * @returns #FALSE if no memory to allocate the child entries + */ +dbus_bool_t +_dbus_object_tree_list_registered_and_unlock (DBusObjectTree *tree, + const char **parent_path, + char ***child_entries) +{ + dbus_bool_t result; + + result = _dbus_object_tree_list_registered_unlocked (tree, + parent_path, + child_entries); + +#ifdef DBUS_BUILD_TESTS + if (tree->connection) +#endif + { + _dbus_verbose ("unlock %s\n", _DBUS_FUNCTION_NAME); + _dbus_connection_unlock (tree->connection); + } + + return result; +} + + +/** Set to 1 to get a bunch of spew about disassembling the path string */ +#define VERBOSE_DECOMPOSE 0 + +/** + * Decompose an object path. A path of just "/" is + * represented as an empty vector of strings. + * The path need not be nul terminated. + * + * @param data the path data + * @param len the length of the path string + * @param path address to store new object path + * @param path_len length of stored path + */ +dbus_bool_t +_dbus_decompose_path (const char* data, + int len, + char ***path, + int *path_len) +{ + char **retval; + int n_components; + int i, j, comp; + + _dbus_assert (data != NULL); + +#if VERBOSE_DECOMPOSE + _dbus_verbose ("Decomposing path \"%s\"\n", + data); +#endif + + n_components = 0; + if (len > 1) /* if path is not just "/" */ + { + i = 0; + while (i < len) + { + if (data[i] == '/') + n_components += 1; + ++i; + } + } + + retval = dbus_new0 (char*, n_components + 1); + + if (retval == NULL) + return FALSE; + + comp = 0; + if (n_components == 0) + i = 1; + else + i = 0; + while (comp < n_components) + { + _dbus_assert (i < len); + + if (data[i] == '/') + ++i; + j = i; + + while (j < len && data[j] != '/') + ++j; + + /* Now [i, j) is the path component */ + _dbus_assert (i < j); + _dbus_assert (data[i] != '/'); + _dbus_assert (j == len || data[j] == '/'); + +#if VERBOSE_DECOMPOSE + _dbus_verbose (" (component in [%d,%d))\n", + i, j); +#endif + + retval[comp] = _dbus_memdup (&data[i], j - i + 1); + if (retval[comp] == NULL) + { + dbus_free_string_array (retval); + return FALSE; + } + retval[comp][j-i] = '\0'; +#if VERBOSE_DECOMPOSE + _dbus_verbose (" (component %d = \"%s\")\n", + comp, retval[comp]); +#endif + + ++comp; + i = j; + } + _dbus_assert (i == len); + + *path = retval; + if (path_len) + *path_len = n_components; + + return TRUE; +} + +/** @} */ + +static char* +flatten_path (const char **path) +{ + DBusString str; + char *s; + + if (!_dbus_string_init (&str)) + return NULL; + + if (path[0] == NULL) + { + if (!_dbus_string_append_byte (&str, '/')) + goto nomem; + } + else + { + int i; + + i = 0; + while (path[i]) + { + if (!_dbus_string_append_byte (&str, '/')) + goto nomem; + + if (!_dbus_string_append (&str, path[i])) + goto nomem; + + ++i; + } + } + + if (!_dbus_string_steal_data (&str, &s)) + goto nomem; + + _dbus_string_free (&str); + + return s; + + nomem: + _dbus_string_free (&str); + return NULL; +} + + +#ifdef DBUS_BUILD_TESTS + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +#include "dbus-test.h" +#include + +typedef enum +{ + STR_EQUAL, + STR_PREFIX, + STR_DIFFERENT +} StrComparison; + +/* Returns TRUE if container is a parent of child + */ +static StrComparison +path_contains (const char **container, + const char **child) +{ + int i; + + i = 0; + while (child[i] != NULL) + { + int v; + + if (container[i] == NULL) + return STR_PREFIX; /* container ran out, child continues; + * thus the container is a parent of the + * child. + */ + + _dbus_assert (container[i] != NULL); + _dbus_assert (child[i] != NULL); + + v = strcmp (container[i], child[i]); + + if (v != 0) + return STR_DIFFERENT; /* they overlap until here and then are different, + * not overlapping + */ + + ++i; + } + + /* Child ran out; if container also did, they are equal; + * otherwise, the child is a parent of the container. + */ + if (container[i] == NULL) + return STR_EQUAL; + else + return STR_DIFFERENT; +} + +#if 0 +static void +spew_subtree_recurse (DBusObjectSubtree *subtree, + int indent) +{ + int i; + + i = 0; + while (i < indent) + { + _dbus_verbose (" "); + ++i; + } + + _dbus_verbose ("%s (%d children)\n", + subtree->name, subtree->n_subtrees); + + i = 0; + while (i < subtree->n_subtrees) + { + spew_subtree_recurse (subtree->subtrees[i], indent + 2); + + ++i; + } +} + +static void +spew_tree (DBusObjectTree *tree) +{ + spew_subtree_recurse (tree->root, 0); +} +#endif + +/** + * Callback data used in tests + */ +typedef struct +{ + const char **path; /**< Path */ + dbus_bool_t handler_fallback; /**< true if the handler may be called as fallback */ + dbus_bool_t message_handled; /**< Gets set to true if message handler called */ + dbus_bool_t handler_unregistered; /**< gets set to true if handler is unregistered */ +} TreeTestData; + + +static void +test_unregister_function (DBusConnection *connection, + void *user_data) +{ + TreeTestData *ttd = user_data; + + ttd->handler_unregistered = TRUE; +} + +static DBusHandlerResult +test_message_function (DBusConnection *connection, + DBusMessage *message, + void *user_data) +{ + TreeTestData *ttd = user_data; + + ttd->message_handled = TRUE; + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static dbus_bool_t +do_register (DBusObjectTree *tree, + const char **path, + dbus_bool_t fallback, + int i, + TreeTestData *tree_test_data) +{ + DBusObjectPathVTable vtable = { test_unregister_function, + test_message_function, NULL }; + + tree_test_data[i].message_handled = FALSE; + tree_test_data[i].handler_unregistered = FALSE; + tree_test_data[i].handler_fallback = fallback; + tree_test_data[i].path = path; + + if (!_dbus_object_tree_register (tree, fallback, path, + &vtable, + &tree_test_data[i], + NULL)) + return FALSE; + + _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path) == + &tree_test_data[i]); + + return TRUE; +} + +static dbus_bool_t +do_test_dispatch (DBusObjectTree *tree, + const char **path, + int i, + TreeTestData *tree_test_data, + int n_test_data) +{ + DBusMessage *message; + int j; + DBusHandlerResult result; + char *flat; + + message = NULL; + + flat = flatten_path (path); + if (flat == NULL) + goto oom; + + message = dbus_message_new_method_call (NULL, + flat, + "org.freedesktop.TestInterface", + "Foo"); + dbus_free (flat); + if (message == NULL) + goto oom; + + j = 0; + while (j < n_test_data) + { + tree_test_data[j].message_handled = FALSE; + ++j; + } + + result = _dbus_object_tree_dispatch_and_unlock (tree, message); + if (result == DBUS_HANDLER_RESULT_NEED_MEMORY) + goto oom; + + _dbus_assert (tree_test_data[i].message_handled); + + j = 0; + while (j < n_test_data) + { + if (tree_test_data[j].message_handled) + { + if (tree_test_data[j].handler_fallback) + _dbus_assert (path_contains (tree_test_data[j].path, + path) != STR_DIFFERENT); + else + _dbus_assert (path_contains (tree_test_data[j].path, path) == STR_EQUAL); + } + else + { + if (tree_test_data[j].handler_fallback) + _dbus_assert (path_contains (tree_test_data[j].path, + path) == STR_DIFFERENT); + else + _dbus_assert (path_contains (tree_test_data[j].path, path) != STR_EQUAL); + } + + ++j; + } + + dbus_message_unref (message); + + return TRUE; + + oom: + if (message) + dbus_message_unref (message); + return FALSE; +} + +static size_t +string_array_length (const char **array) +{ + size_t i; + for (i = 0; array[i]; i++) ; + return i; +} + +typedef struct +{ + const char *path; + const char *result[20]; +} DecomposePathTest; + +static DecomposePathTest decompose_tests[] = { + { "/foo", { "foo", NULL } }, + { "/foo/bar", { "foo", "bar", NULL } }, + { "/", { NULL } }, + { "/a/b", { "a", "b", NULL } }, + { "/a/b/c", { "a", "b", "c", NULL } }, + { "/a/b/c/d", { "a", "b", "c", "d", NULL } }, + { "/foo/bar/q", { "foo", "bar", "q", NULL } }, + { "/foo/bar/this/is/longer", { "foo", "bar", "this", "is", "longer", NULL } } +}; + +static dbus_bool_t +run_decompose_tests (void) +{ + int i; + + i = 0; + while (i < _DBUS_N_ELEMENTS (decompose_tests)) + { + char **result; + int result_len; + int expected_len; + + if (!_dbus_decompose_path (decompose_tests[i].path, + strlen (decompose_tests[i].path), + &result, &result_len)) + return FALSE; + + expected_len = string_array_length (decompose_tests[i].result); + + if (result_len != (int) string_array_length ((const char**)result) || + expected_len != result_len || + path_contains (decompose_tests[i].result, + (const char**) result) != STR_EQUAL) + { + int real_len = string_array_length ((const char**)result); + _dbus_warn ("Expected decompose of %s to have len %d, returned %d, appears to have %d\n", + decompose_tests[i].path, expected_len, result_len, + real_len); + _dbus_warn ("Decompose resulted in elements: { "); + i = 0; + while (i < real_len) + { + _dbus_warn ("\"%s\"%s", result[i], + (i + 1) == real_len ? "" : ", "); + ++i; + } + _dbus_warn ("}\n"); + _dbus_assert_not_reached ("path decompose failed\n"); + } + + dbus_free_string_array (result); + + ++i; + } + + return TRUE; +} + +static dbus_bool_t +object_tree_test_iteration (void *data) +{ + const char *path0[] = { NULL }; + const char *path1[] = { "foo", NULL }; + const char *path2[] = { "foo", "bar", NULL }; + const char *path3[] = { "foo", "bar", "baz", NULL }; + const char *path4[] = { "foo", "bar", "boo", NULL }; + const char *path5[] = { "blah", NULL }; + const char *path6[] = { "blah", "boof", NULL }; + const char *path7[] = { "blah", "boof", "this", "is", "really", "long", NULL }; + const char *path8[] = { "childless", NULL }; + DBusObjectTree *tree; + TreeTestData tree_test_data[9]; + int i; + dbus_bool_t exact_match; + + if (!run_decompose_tests ()) + return FALSE; + + tree = NULL; + + tree = _dbus_object_tree_new (NULL); + if (tree == NULL) + goto out; + + if (!do_register (tree, path0, TRUE, 0, tree_test_data)) + goto out; + + _dbus_assert (find_subtree (tree, path0, NULL)); + _dbus_assert (!find_subtree (tree, path1, NULL)); + _dbus_assert (!find_subtree (tree, path2, NULL)); + _dbus_assert (!find_subtree (tree, path3, NULL)); + _dbus_assert (!find_subtree (tree, path4, NULL)); + _dbus_assert (!find_subtree (tree, path5, NULL)); + _dbus_assert (!find_subtree (tree, path6, NULL)); + _dbus_assert (!find_subtree (tree, path7, NULL)); + _dbus_assert (!find_subtree (tree, path8, NULL)); + + _dbus_assert (find_handler (tree, path0, &exact_match) && exact_match); + _dbus_assert (find_handler (tree, path1, &exact_match) == tree->root && !exact_match); + _dbus_assert (find_handler (tree, path2, &exact_match) == tree->root && !exact_match); + _dbus_assert (find_handler (tree, path3, &exact_match) == tree->root && !exact_match); + _dbus_assert (find_handler (tree, path4, &exact_match) == tree->root && !exact_match); + _dbus_assert (find_handler (tree, path5, &exact_match) == tree->root && !exact_match); + _dbus_assert (find_handler (tree, path6, &exact_match) == tree->root && !exact_match); + _dbus_assert (find_handler (tree, path7, &exact_match) == tree->root && !exact_match); + _dbus_assert (find_handler (tree, path8, &exact_match) == tree->root && !exact_match); + + if (!do_register (tree, path1, TRUE, 1, tree_test_data)) + goto out; + + _dbus_assert (find_subtree (tree, path0, NULL)); + _dbus_assert (find_subtree (tree, path1, NULL)); + _dbus_assert (!find_subtree (tree, path2, NULL)); + _dbus_assert (!find_subtree (tree, path3, NULL)); + _dbus_assert (!find_subtree (tree, path4, NULL)); + _dbus_assert (!find_subtree (tree, path5, NULL)); + _dbus_assert (!find_subtree (tree, path6, NULL)); + _dbus_assert (!find_subtree (tree, path7, NULL)); + _dbus_assert (!find_subtree (tree, path8, NULL)); + + _dbus_assert (find_handler (tree, path0, &exact_match) && exact_match); + _dbus_assert (find_handler (tree, path1, &exact_match) && exact_match); + _dbus_assert (find_handler (tree, path2, &exact_match) && !exact_match); + _dbus_assert (find_handler (tree, path3, &exact_match) && !exact_match); + _dbus_assert (find_handler (tree, path4, &exact_match) && !exact_match); + _dbus_assert (find_handler (tree, path5, &exact_match) == tree->root && !exact_match); + _dbus_assert (find_handler (tree, path6, &exact_match) == tree->root && !exact_match); + _dbus_assert (find_handler (tree, path7, &exact_match) == tree->root && !exact_match); + _dbus_assert (find_handler (tree, path8, &exact_match) == tree->root && !exact_match); + + if (!do_register (tree, path2, TRUE, 2, tree_test_data)) + goto out; + + _dbus_assert (find_subtree (tree, path1, NULL)); + _dbus_assert (find_subtree (tree, path2, NULL)); + _dbus_assert (!find_subtree (tree, path3, NULL)); + _dbus_assert (!find_subtree (tree, path4, NULL)); + _dbus_assert (!find_subtree (tree, path5, NULL)); + _dbus_assert (!find_subtree (tree, path6, NULL)); + _dbus_assert (!find_subtree (tree, path7, NULL)); + _dbus_assert (!find_subtree (tree, path8, NULL)); + + if (!do_register (tree, path3, TRUE, 3, tree_test_data)) + goto out; + + _dbus_assert (find_subtree (tree, path0, NULL)); + _dbus_assert (find_subtree (tree, path1, NULL)); + _dbus_assert (find_subtree (tree, path2, NULL)); + _dbus_assert (find_subtree (tree, path3, NULL)); + _dbus_assert (!find_subtree (tree, path4, NULL)); + _dbus_assert (!find_subtree (tree, path5, NULL)); + _dbus_assert (!find_subtree (tree, path6, NULL)); + _dbus_assert (!find_subtree (tree, path7, NULL)); + _dbus_assert (!find_subtree (tree, path8, NULL)); + + if (!do_register (tree, path4, TRUE, 4, tree_test_data)) + goto out; + + _dbus_assert (find_subtree (tree, path0, NULL)); + _dbus_assert (find_subtree (tree, path1, NULL)); + _dbus_assert (find_subtree (tree, path2, NULL)); + _dbus_assert (find_subtree (tree, path3, NULL)); + _dbus_assert (find_subtree (tree, path4, NULL)); + _dbus_assert (!find_subtree (tree, path5, NULL)); + _dbus_assert (!find_subtree (tree, path6, NULL)); + _dbus_assert (!find_subtree (tree, path7, NULL)); + _dbus_assert (!find_subtree (tree, path8, NULL)); + + if (!do_register (tree, path5, TRUE, 5, tree_test_data)) + goto out; + + _dbus_assert (find_subtree (tree, path0, NULL)); + _dbus_assert (find_subtree (tree, path1, NULL)); + _dbus_assert (find_subtree (tree, path2, NULL)); + _dbus_assert (find_subtree (tree, path3, NULL)); + _dbus_assert (find_subtree (tree, path4, NULL)); + _dbus_assert (find_subtree (tree, path5, NULL)); + _dbus_assert (!find_subtree (tree, path6, NULL)); + _dbus_assert (!find_subtree (tree, path7, NULL)); + _dbus_assert (!find_subtree (tree, path8, NULL)); + + _dbus_assert (find_handler (tree, path0, &exact_match) == tree->root && exact_match); + _dbus_assert (find_handler (tree, path1, &exact_match) != tree->root && exact_match); + _dbus_assert (find_handler (tree, path2, &exact_match) != tree->root && exact_match); + _dbus_assert (find_handler (tree, path3, &exact_match) != tree->root && exact_match); + _dbus_assert (find_handler (tree, path4, &exact_match) != tree->root && exact_match); + _dbus_assert (find_handler (tree, path5, &exact_match) != tree->root && exact_match); + _dbus_assert (find_handler (tree, path6, &exact_match) != tree->root && !exact_match); + _dbus_assert (find_handler (tree, path7, &exact_match) != tree->root && !exact_match); + _dbus_assert (find_handler (tree, path8, &exact_match) == tree->root && !exact_match); + + if (!do_register (tree, path6, TRUE, 6, tree_test_data)) + goto out; + + _dbus_assert (find_subtree (tree, path0, NULL)); + _dbus_assert (find_subtree (tree, path1, NULL)); + _dbus_assert (find_subtree (tree, path2, NULL)); + _dbus_assert (find_subtree (tree, path3, NULL)); + _dbus_assert (find_subtree (tree, path4, NULL)); + _dbus_assert (find_subtree (tree, path5, NULL)); + _dbus_assert (find_subtree (tree, path6, NULL)); + _dbus_assert (!find_subtree (tree, path7, NULL)); + _dbus_assert (!find_subtree (tree, path8, NULL)); + + if (!do_register (tree, path7, TRUE, 7, tree_test_data)) + goto out; + + _dbus_assert (find_subtree (tree, path0, NULL)); + _dbus_assert (find_subtree (tree, path1, NULL)); + _dbus_assert (find_subtree (tree, path2, NULL)); + _dbus_assert (find_subtree (tree, path3, NULL)); + _dbus_assert (find_subtree (tree, path4, NULL)); + _dbus_assert (find_subtree (tree, path5, NULL)); + _dbus_assert (find_subtree (tree, path6, NULL)); + _dbus_assert (find_subtree (tree, path7, NULL)); + _dbus_assert (!find_subtree (tree, path8, NULL)); + + if (!do_register (tree, path8, TRUE, 8, tree_test_data)) + goto out; + + _dbus_assert (find_subtree (tree, path0, NULL)); + _dbus_assert (find_subtree (tree, path1, NULL)); + _dbus_assert (find_subtree (tree, path2, NULL)); + _dbus_assert (find_subtree (tree, path3, NULL)); + _dbus_assert (find_subtree (tree, path4, NULL)); + _dbus_assert (find_subtree (tree, path5, NULL)); + _dbus_assert (find_subtree (tree, path6, NULL)); + _dbus_assert (find_subtree (tree, path7, NULL)); + _dbus_assert (find_subtree (tree, path8, NULL)); + + _dbus_assert (find_handler (tree, path0, &exact_match) == tree->root && exact_match); + _dbus_assert (find_handler (tree, path1, &exact_match) != tree->root && exact_match); + _dbus_assert (find_handler (tree, path2, &exact_match) != tree->root && exact_match); + _dbus_assert (find_handler (tree, path3, &exact_match) != tree->root && exact_match); + _dbus_assert (find_handler (tree, path4, &exact_match) != tree->root && exact_match); + _dbus_assert (find_handler (tree, path5, &exact_match) != tree->root && exact_match); + _dbus_assert (find_handler (tree, path6, &exact_match) != tree->root && exact_match); + _dbus_assert (find_handler (tree, path7, &exact_match) != tree->root && exact_match); + _dbus_assert (find_handler (tree, path8, &exact_match) != tree->root && exact_match); + + /* test the list_registered function */ + + { + const char *root[] = { NULL }; + char **child_entries; + int nb; + + _dbus_object_tree_list_registered_unlocked (tree, path1, &child_entries); + if (child_entries != NULL) + { + nb = string_array_length ((const char**)child_entries); + _dbus_assert (nb == 1); + dbus_free_string_array (child_entries); + } + + _dbus_object_tree_list_registered_unlocked (tree, path2, &child_entries); + if (child_entries != NULL) + { + nb = string_array_length ((const char**)child_entries); + _dbus_assert (nb == 2); + dbus_free_string_array (child_entries); + } + + _dbus_object_tree_list_registered_unlocked (tree, path8, &child_entries); + if (child_entries != NULL) + { + nb = string_array_length ((const char**)child_entries); + _dbus_assert (nb == 0); + dbus_free_string_array (child_entries); + } + + _dbus_object_tree_list_registered_unlocked (tree, root, &child_entries); + if (child_entries != NULL) + { + nb = string_array_length ((const char**)child_entries); + _dbus_assert (nb == 3); + dbus_free_string_array (child_entries); + } + } + + /* Check that destroying tree calls unregister funcs */ + _dbus_object_tree_unref (tree); + + i = 0; + while (i < (int) _DBUS_N_ELEMENTS (tree_test_data)) + { + _dbus_assert (tree_test_data[i].handler_unregistered); + _dbus_assert (!tree_test_data[i].message_handled); + ++i; + } + + /* Now start again and try the individual unregister function */ + tree = _dbus_object_tree_new (NULL); + if (tree == NULL) + goto out; + + if (!do_register (tree, path0, TRUE, 0, tree_test_data)) + goto out; + if (!do_register (tree, path1, TRUE, 1, tree_test_data)) + goto out; + if (!do_register (tree, path2, TRUE, 2, tree_test_data)) + goto out; + if (!do_register (tree, path3, TRUE, 3, tree_test_data)) + goto out; + if (!do_register (tree, path4, TRUE, 4, tree_test_data)) + goto out; + if (!do_register (tree, path5, TRUE, 5, tree_test_data)) + goto out; + if (!do_register (tree, path6, TRUE, 6, tree_test_data)) + goto out; + if (!do_register (tree, path7, TRUE, 7, tree_test_data)) + goto out; + if (!do_register (tree, path8, TRUE, 8, tree_test_data)) + goto out; + + _dbus_object_tree_unregister_and_unlock (tree, path0); + _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path0) == NULL); + + _dbus_assert (!find_subtree (tree, path0, NULL)); + _dbus_assert (find_subtree (tree, path1, NULL)); + _dbus_assert (find_subtree (tree, path2, NULL)); + _dbus_assert (find_subtree (tree, path3, NULL)); + _dbus_assert (find_subtree (tree, path4, NULL)); + _dbus_assert (find_subtree (tree, path5, NULL)); + _dbus_assert (find_subtree (tree, path6, NULL)); + _dbus_assert (find_subtree (tree, path7, NULL)); + _dbus_assert (find_subtree (tree, path8, NULL)); + + _dbus_object_tree_unregister_and_unlock (tree, path1); + _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path1) == NULL); + + _dbus_assert (!find_subtree (tree, path0, NULL)); + _dbus_assert (!find_subtree (tree, path1, NULL)); + _dbus_assert (find_subtree (tree, path2, NULL)); + _dbus_assert (find_subtree (tree, path3, NULL)); + _dbus_assert (find_subtree (tree, path4, NULL)); + _dbus_assert (find_subtree (tree, path5, NULL)); + _dbus_assert (find_subtree (tree, path6, NULL)); + _dbus_assert (find_subtree (tree, path7, NULL)); + _dbus_assert (find_subtree (tree, path8, NULL)); + + _dbus_object_tree_unregister_and_unlock (tree, path2); + _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path2) == NULL); + + _dbus_assert (!find_subtree (tree, path0, NULL)); + _dbus_assert (!find_subtree (tree, path1, NULL)); + _dbus_assert (!find_subtree (tree, path2, NULL)); + _dbus_assert (find_subtree (tree, path3, NULL)); + _dbus_assert (find_subtree (tree, path4, NULL)); + _dbus_assert (find_subtree (tree, path5, NULL)); + _dbus_assert (find_subtree (tree, path6, NULL)); + _dbus_assert (find_subtree (tree, path7, NULL)); + _dbus_assert (find_subtree (tree, path8, NULL)); + + _dbus_object_tree_unregister_and_unlock (tree, path3); + _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path3) == NULL); + + _dbus_assert (!find_subtree (tree, path0, NULL)); + _dbus_assert (!find_subtree (tree, path1, NULL)); + _dbus_assert (!find_subtree (tree, path2, NULL)); + _dbus_assert (!find_subtree (tree, path3, NULL)); + _dbus_assert (find_subtree (tree, path4, NULL)); + _dbus_assert (find_subtree (tree, path5, NULL)); + _dbus_assert (find_subtree (tree, path6, NULL)); + _dbus_assert (find_subtree (tree, path7, NULL)); + _dbus_assert (find_subtree (tree, path8, NULL)); + + _dbus_object_tree_unregister_and_unlock (tree, path4); + _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path4) == NULL); + + _dbus_assert (!find_subtree (tree, path0, NULL)); + _dbus_assert (!find_subtree (tree, path1, NULL)); + _dbus_assert (!find_subtree (tree, path2, NULL)); + _dbus_assert (!find_subtree (tree, path3, NULL)); + _dbus_assert (!find_subtree (tree, path4, NULL)); + _dbus_assert (find_subtree (tree, path5, NULL)); + _dbus_assert (find_subtree (tree, path6, NULL)); + _dbus_assert (find_subtree (tree, path7, NULL)); + _dbus_assert (find_subtree (tree, path8, NULL)); + + _dbus_object_tree_unregister_and_unlock (tree, path5); + _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path5) == NULL); + + _dbus_assert (!find_subtree (tree, path0, NULL)); + _dbus_assert (!find_subtree (tree, path1, NULL)); + _dbus_assert (!find_subtree (tree, path2, NULL)); + _dbus_assert (!find_subtree (tree, path3, NULL)); + _dbus_assert (!find_subtree (tree, path4, NULL)); + _dbus_assert (!find_subtree (tree, path5, NULL)); + _dbus_assert (find_subtree (tree, path6, NULL)); + _dbus_assert (find_subtree (tree, path7, NULL)); + _dbus_assert (find_subtree (tree, path8, NULL)); + + _dbus_object_tree_unregister_and_unlock (tree, path6); + _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path6) == NULL); + + _dbus_assert (!find_subtree (tree, path0, NULL)); + _dbus_assert (!find_subtree (tree, path1, NULL)); + _dbus_assert (!find_subtree (tree, path2, NULL)); + _dbus_assert (!find_subtree (tree, path3, NULL)); + _dbus_assert (!find_subtree (tree, path4, NULL)); + _dbus_assert (!find_subtree (tree, path5, NULL)); + _dbus_assert (!find_subtree (tree, path6, NULL)); + _dbus_assert (find_subtree (tree, path7, NULL)); + _dbus_assert (find_subtree (tree, path8, NULL)); + + _dbus_object_tree_unregister_and_unlock (tree, path7); + _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path7) == NULL); + + _dbus_assert (!find_subtree (tree, path0, NULL)); + _dbus_assert (!find_subtree (tree, path1, NULL)); + _dbus_assert (!find_subtree (tree, path2, NULL)); + _dbus_assert (!find_subtree (tree, path3, NULL)); + _dbus_assert (!find_subtree (tree, path4, NULL)); + _dbus_assert (!find_subtree (tree, path5, NULL)); + _dbus_assert (!find_subtree (tree, path6, NULL)); + _dbus_assert (!find_subtree (tree, path7, NULL)); + _dbus_assert (find_subtree (tree, path8, NULL)); + + _dbus_object_tree_unregister_and_unlock (tree, path8); + _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path8) == NULL); + + _dbus_assert (!find_subtree (tree, path0, NULL)); + _dbus_assert (!find_subtree (tree, path1, NULL)); + _dbus_assert (!find_subtree (tree, path2, NULL)); + _dbus_assert (!find_subtree (tree, path3, NULL)); + _dbus_assert (!find_subtree (tree, path4, NULL)); + _dbus_assert (!find_subtree (tree, path5, NULL)); + _dbus_assert (!find_subtree (tree, path6, NULL)); + _dbus_assert (!find_subtree (tree, path7, NULL)); + _dbus_assert (!find_subtree (tree, path8, NULL)); + + i = 0; + while (i < (int) _DBUS_N_ELEMENTS (tree_test_data)) + { + _dbus_assert (tree_test_data[i].handler_unregistered); + _dbus_assert (!tree_test_data[i].message_handled); + ++i; + } + + /* Register it all again, and test dispatch */ + + if (!do_register (tree, path0, TRUE, 0, tree_test_data)) + goto out; + if (!do_register (tree, path1, FALSE, 1, tree_test_data)) + goto out; + if (!do_register (tree, path2, TRUE, 2, tree_test_data)) + goto out; + if (!do_register (tree, path3, TRUE, 3, tree_test_data)) + goto out; + if (!do_register (tree, path4, TRUE, 4, tree_test_data)) + goto out; + if (!do_register (tree, path5, TRUE, 5, tree_test_data)) + goto out; + if (!do_register (tree, path6, FALSE, 6, tree_test_data)) + goto out; + if (!do_register (tree, path7, TRUE, 7, tree_test_data)) + goto out; + if (!do_register (tree, path8, TRUE, 8, tree_test_data)) + goto out; + +#if 0 + spew_tree (tree); +#endif + + if (!do_test_dispatch (tree, path0, 0, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data))) + goto out; + if (!do_test_dispatch (tree, path1, 1, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data))) + goto out; + if (!do_test_dispatch (tree, path2, 2, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data))) + goto out; + if (!do_test_dispatch (tree, path3, 3, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data))) + goto out; + if (!do_test_dispatch (tree, path4, 4, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data))) + goto out; + if (!do_test_dispatch (tree, path5, 5, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data))) + goto out; + if (!do_test_dispatch (tree, path6, 6, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data))) + goto out; + if (!do_test_dispatch (tree, path7, 7, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data))) + goto out; + if (!do_test_dispatch (tree, path8, 8, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data))) + goto out; + + out: + if (tree) + { + /* test ref */ + _dbus_object_tree_ref (tree); + _dbus_object_tree_unref (tree); + _dbus_object_tree_unref (tree); + } + + return TRUE; +} + +/** + * @ingroup DBusObjectTree + * Unit test for DBusObjectTree + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_object_tree_test (void) +{ + _dbus_test_oom_handling ("object tree", + object_tree_test_iteration, + NULL); + + return TRUE; +} + +#endif /* !DOXYGEN_SHOULD_SKIP_THIS */ + +#endif /* DBUS_BUILD_TESTS */ diff --git a/src/dbus/dbus-object-tree.h b/src/dbus/dbus-object-tree.h new file mode 100644 index 0000000..1166752 --- /dev/null +++ b/src/dbus/dbus-object-tree.h @@ -0,0 +1,62 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-object-tree.h DBusObjectTree (internals of DBusConnection) + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef DBUS_OBJECT_TREE_H +#define DBUS_OBJECT_TREE_H + +#include + +DBUS_BEGIN_DECLS + +typedef struct DBusObjectTree DBusObjectTree; + +DBusObjectTree* _dbus_object_tree_new (DBusConnection *connection); +DBusObjectTree* _dbus_object_tree_ref (DBusObjectTree *tree); +void _dbus_object_tree_unref (DBusObjectTree *tree); + +dbus_bool_t _dbus_object_tree_register (DBusObjectTree *tree, + dbus_bool_t fallback, + const char **path, + const DBusObjectPathVTable *vtable, + void *user_data, + DBusError *error); +void _dbus_object_tree_unregister_and_unlock (DBusObjectTree *tree, + const char **path); +DBusHandlerResult _dbus_object_tree_dispatch_and_unlock (DBusObjectTree *tree, + DBusMessage *message); +void* _dbus_object_tree_get_user_data_unlocked (DBusObjectTree *tree, + const char **path); +void _dbus_object_tree_free_all_unlocked (DBusObjectTree *tree); + + +dbus_bool_t _dbus_object_tree_list_registered_and_unlock (DBusObjectTree *tree, + const char **parent_path, + char ***child_entries); + +dbus_bool_t _dbus_decompose_path (const char *data, + int len, + char ***path, + int *path_len); + +DBUS_END_DECLS + +#endif /* DBUS_OBJECT_TREE_H */ diff --git a/src/dbus/dbus-pending-call-internal.h b/src/dbus/dbus-pending-call-internal.h new file mode 100644 index 0000000..05374a6 --- /dev/null +++ b/src/dbus/dbus-pending-call-internal.h @@ -0,0 +1,67 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-pending-call-internal.h DBusPendingCall internal interfaces + * + * Copyright (C) 2002 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef DBUS_PENDING_CALL_INTERNAL_H +#define DBUS_PENDING_CALL_INTERNAL_H + + +#include +#include +#include +#include + +DBUS_BEGIN_DECLS + +dbus_bool_t _dbus_pending_call_is_timeout_added_unlocked (DBusPendingCall *pending); +void _dbus_pending_call_set_timeout_added_unlocked (DBusPendingCall *pending, + dbus_bool_t is_added); +DBusTimeout * _dbus_pending_call_get_timeout_unlocked (DBusPendingCall *pending); +dbus_uint32_t _dbus_pending_call_get_reply_serial_unlocked (DBusPendingCall *pending); +void _dbus_pending_call_set_reply_serial_unlocked (DBusPendingCall *pending, + dbus_uint32_t serial); +DBusConnection * _dbus_pending_call_get_connection_and_lock (DBusPendingCall *pending); +DBusConnection * _dbus_pending_call_get_connection_unlocked (DBusPendingCall *pending); +dbus_bool_t _dbus_pending_call_get_completed_unlocked (DBusPendingCall *pending); +void _dbus_pending_call_complete (DBusPendingCall *pending); +void _dbus_pending_call_set_reply_unlocked (DBusPendingCall *pending, + DBusMessage *message); +void _dbus_pending_call_queue_timeout_error_unlocked (DBusPendingCall *pending, + DBusConnection *connection); +void _dbus_pending_call_set_reply_serial_unlocked (DBusPendingCall *pending, + dbus_uint32_t serial); +dbus_bool_t _dbus_pending_call_set_timeout_error_unlocked (DBusPendingCall *pending, + DBusMessage *message, + dbus_uint32_t serial); +DBusPendingCall* _dbus_pending_call_new_unlocked (DBusConnection *connection, + int timeout_milliseconds, + DBusTimeoutHandler timeout_handler); +DBusPendingCall* _dbus_pending_call_ref_unlocked (DBusPendingCall *pending); +void _dbus_pending_call_unref_and_unlock (DBusPendingCall *pending); +dbus_bool_t _dbus_pending_call_set_data_unlocked (DBusPendingCall *pending, + dbus_int32_t slot, + void *data, + DBusFreeFunction free_data_func); + + +DBUS_END_DECLS + +#endif /* DBUS_PENDING_CALL_INTERNAL_H */ diff --git a/src/dbus/dbus-pending-call.c b/src/dbus/dbus-pending-call.c new file mode 100644 index 0000000..51b9378 --- /dev/null +++ b/src/dbus/dbus-pending-call.c @@ -0,0 +1,826 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-pending-call.c Object representing a call in progress. + * + * Copyright (C) 2002, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-internals.h" +#include "dbus-connection-internal.h" +#include "dbus-pending-call-internal.h" +#include "dbus-pending-call.h" +#include "dbus-list.h" +#include "dbus-threads.h" +#include "dbus-test.h" + +/** + * @defgroup DBusPendingCallInternals DBusPendingCall implementation details + * @ingroup DBusInternals + * @brief DBusPendingCall private implementation details. + * + * The guts of DBusPendingCall and its methods. + * + * @{ + */ + +/** + * @brief Internals of DBusPendingCall + * + * Opaque object representing a reply message that we're waiting for. + */ + +/** + * shorter and more visible way to write _dbus_connection_lock() + */ +#define CONNECTION_LOCK(connection) _dbus_connection_lock(connection) +/** + * shorter and more visible way to write _dbus_connection_unlock() + */ +#define CONNECTION_UNLOCK(connection) _dbus_connection_unlock(connection) + +/** + * Implementation details of #DBusPendingCall - all fields are private. + */ +struct DBusPendingCall +{ + DBusAtomic refcount; /**< reference count */ + + DBusDataSlotList slot_list; /**< Data stored by allocated integer ID */ + + DBusPendingCallNotifyFunction function; /**< Notifier when reply arrives. */ + + DBusConnection *connection; /**< Connections we're associated with */ + DBusMessage *reply; /**< Reply (after we've received it) */ + DBusTimeout *timeout; /**< Timeout */ + + DBusList *timeout_link; /**< Preallocated timeout response */ + + dbus_uint32_t reply_serial; /**< Expected serial of reply */ + + unsigned int completed : 1; /**< TRUE if completed */ + unsigned int timeout_added : 1; /**< Have added the timeout */ +}; + +static dbus_int32_t notify_user_data_slot = -1; + +/** + * Creates a new pending reply object. + * + * @param connection connection where reply will arrive + * @param timeout_milliseconds length of timeout, -1 for default + * @param timeout_handler timeout handler, takes pending call as data + * @returns a new #DBusPendingCall or #NULL if no memory. + */ +DBusPendingCall* +_dbus_pending_call_new_unlocked (DBusConnection *connection, + int timeout_milliseconds, + DBusTimeoutHandler timeout_handler) +{ + DBusPendingCall *pending; + DBusTimeout *timeout; + + _dbus_assert (timeout_milliseconds >= 0 || timeout_milliseconds == -1); + + if (timeout_milliseconds == -1) + timeout_milliseconds = _DBUS_DEFAULT_TIMEOUT_VALUE; + + /* it would probably seem logical to pass in _DBUS_INT_MAX for + * infinite timeout, but then math in + * _dbus_connection_block_for_reply would get all overflow-prone, so + * smack that down. + */ + if (timeout_milliseconds > _DBUS_ONE_HOUR_IN_MILLISECONDS * 6) + timeout_milliseconds = _DBUS_ONE_HOUR_IN_MILLISECONDS * 6; + + if (!dbus_pending_call_allocate_data_slot (¬ify_user_data_slot)) + return NULL; + + pending = dbus_new0 (DBusPendingCall, 1); + + if (pending == NULL) + { + dbus_pending_call_free_data_slot (¬ify_user_data_slot); + return NULL; + } + + timeout = _dbus_timeout_new (timeout_milliseconds, + timeout_handler, + pending, NULL); + + if (timeout == NULL) + { + dbus_pending_call_free_data_slot (¬ify_user_data_slot); + dbus_free (pending); + return NULL; + } + + pending->refcount.value = 1; + pending->connection = connection; + _dbus_connection_ref_unlocked (pending->connection); + + pending->timeout = timeout; + + + _dbus_data_slot_list_init (&pending->slot_list); + + return pending; +} + +/** + * Sets the reply of a pending call with the given message, + * or if the message is #NULL, by timing out the pending call. + * + * @param pending the pending call + * @param message the message to complete the call with, or #NULL + * to time out the call + */ +void +_dbus_pending_call_set_reply_unlocked (DBusPendingCall *pending, + DBusMessage *message) +{ + if (message == NULL) + { + message = pending->timeout_link->data; + _dbus_list_clear (&pending->timeout_link); + } + else + dbus_message_ref (message); + + _dbus_verbose (" handing message %p (%s) to pending call serial %u\n", + message, + dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_RETURN ? + "method return" : + dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR ? + "error" : "other type", + pending->reply_serial); + + _dbus_assert (pending->reply == NULL); + _dbus_assert (pending->reply_serial == dbus_message_get_reply_serial (message)); + pending->reply = message; +} + +/** + * Calls notifier function for the pending call + * and sets the call to completed. + * + * @param pending the pending call + * + */ +void +_dbus_pending_call_complete (DBusPendingCall *pending) +{ + _dbus_assert (!pending->completed); + + pending->completed = TRUE; + + if (pending->function) + { + void *user_data; + user_data = dbus_pending_call_get_data (pending, + notify_user_data_slot); + + (* pending->function) (pending, user_data); + } +} + +/** + * If the pending call hasn't been timed out, add its timeout + * error reply to the connection's incoming message queue. + * + * @param pending the pending call + * @param connection the connection the call was sent to + */ +void +_dbus_pending_call_queue_timeout_error_unlocked (DBusPendingCall *pending, + DBusConnection *connection) +{ + _dbus_assert (connection == pending->connection); + + if (pending->timeout_link) + { + _dbus_connection_queue_synthesized_message_link (connection, + pending->timeout_link); + pending->timeout_link = NULL; + } +} + +/** + * Checks to see if a timeout has been added + * + * @param pending the pending_call + * @returns #TRUE if there is a timeout or #FALSE if not + */ +dbus_bool_t +_dbus_pending_call_is_timeout_added_unlocked (DBusPendingCall *pending) +{ + _dbus_assert (pending != NULL); + + return pending->timeout_added; +} + + +/** + * Sets wether the timeout has been added + * + * @param pending the pending_call + * @param is_added whether or not a timeout is added + */ +void +_dbus_pending_call_set_timeout_added_unlocked (DBusPendingCall *pending, + dbus_bool_t is_added) +{ + _dbus_assert (pending != NULL); + + pending->timeout_added = is_added; +} + + +/** + * Retrives the timeout + * + * @param pending the pending_call + * @returns a timeout object + */ +DBusTimeout * +_dbus_pending_call_get_timeout_unlocked (DBusPendingCall *pending) +{ + _dbus_assert (pending != NULL); + + return pending->timeout; +} + +/** + * Gets the reply's serial number + * + * @param pending the pending_call + * @returns a serial number for the reply or 0 + */ +dbus_uint32_t +_dbus_pending_call_get_reply_serial_unlocked (DBusPendingCall *pending) +{ + _dbus_assert (pending != NULL); + + return pending->reply_serial; +} + +/** + * Sets the reply's serial number + * + * @param pending the pending_call + * @param serial the serial number + */ +void +_dbus_pending_call_set_reply_serial_unlocked (DBusPendingCall *pending, + dbus_uint32_t serial) +{ + _dbus_assert (pending != NULL); + _dbus_assert (pending->reply_serial == 0); + + pending->reply_serial = serial; +} + +/** + * Gets the connection associated with this pending call. + * + * @param pending the pending_call + * @returns the connection associated with the pending call + */ +DBusConnection * +_dbus_pending_call_get_connection_and_lock (DBusPendingCall *pending) +{ + _dbus_assert (pending != NULL); + + CONNECTION_LOCK (pending->connection); + return pending->connection; +} + +/** + * Gets the connection associated with this pending call. + * + * @param pending the pending_call + * @returns the connection associated with the pending call + */ +DBusConnection * +_dbus_pending_call_get_connection_unlocked (DBusPendingCall *pending) +{ + _dbus_assert (pending != NULL); + + return pending->connection; +} + +/** + * Sets the reply message associated with the pending call to a timeout error + * + * @param pending the pending_call + * @param message the message we are sending the error reply to + * @param serial serial number for the reply + * @return #FALSE on OOM + */ +dbus_bool_t +_dbus_pending_call_set_timeout_error_unlocked (DBusPendingCall *pending, + DBusMessage *message, + dbus_uint32_t serial) +{ + DBusList *reply_link; + DBusMessage *reply; + + reply = dbus_message_new_error (message, DBUS_ERROR_NO_REPLY, + "Did not receive a reply. Possible causes include: " + "the remote application did not send a reply, " + "the message bus security policy blocked the reply, " + "the reply timeout expired, or " + "the network connection was broken."); + if (reply == NULL) + return FALSE; + + reply_link = _dbus_list_alloc_link (reply); + if (reply_link == NULL) + { + dbus_message_unref (reply); + return FALSE; + } + + pending->timeout_link = reply_link; + + _dbus_pending_call_set_reply_serial_unlocked (pending, serial); + + return TRUE; +} + +/** + * Increments the reference count on a pending call, + * while the lock on its connection is already held. + * + * @param pending the pending call object + * @returns the pending call object + */ +DBusPendingCall * +_dbus_pending_call_ref_unlocked (DBusPendingCall *pending) +{ + pending->refcount.value += 1; + + return pending; +} + + +static void +_dbus_pending_call_last_unref (DBusPendingCall *pending) +{ + DBusConnection *connection; + + /* If we get here, we should be already detached + * from the connection, or never attached. + */ + _dbus_assert (!pending->timeout_added); + + connection = pending->connection; + + /* this assumes we aren't holding connection lock... */ + _dbus_data_slot_list_free (&pending->slot_list); + + if (pending->timeout != NULL) + _dbus_timeout_unref (pending->timeout); + + if (pending->timeout_link) + { + dbus_message_unref ((DBusMessage *)pending->timeout_link->data); + _dbus_list_free_link (pending->timeout_link); + pending->timeout_link = NULL; + } + + if (pending->reply) + { + dbus_message_unref (pending->reply); + pending->reply = NULL; + } + + dbus_free (pending); + + dbus_pending_call_free_data_slot (¬ify_user_data_slot); + + /* connection lock should not be held. */ + /* Free the connection last to avoid a weird state while + * calling out to application code where the pending exists + * but not the connection. + */ + dbus_connection_unref (connection); +} + +/** + * Decrements the reference count on a pending call, + * freeing it if the count reaches 0. Assumes + * connection lock is already held. + * + * @param pending the pending call object + */ +void +_dbus_pending_call_unref_and_unlock (DBusPendingCall *pending) +{ + dbus_bool_t last_unref; + + _dbus_assert (pending->refcount.value > 0); + + pending->refcount.value -= 1; + last_unref = pending->refcount.value == 0; + + CONNECTION_UNLOCK (pending->connection); + if (last_unref) + _dbus_pending_call_last_unref (pending); +} + +/** + * Checks whether the pending call has received a reply + * yet, or not. Assumes connection lock is held. + * + * @param pending the pending call + * @returns #TRUE if a reply has been received + */ +dbus_bool_t +_dbus_pending_call_get_completed_unlocked (DBusPendingCall *pending) +{ + return pending->completed; +} + +static DBusDataSlotAllocator slot_allocator; +_DBUS_DEFINE_GLOBAL_LOCK (pending_call_slots); + +/** + * Stores a pointer on a #DBusPendingCall, along + * with an optional function to be used for freeing + * the data when the data is set again, or when + * the pending call is finalized. The slot number + * must have been allocated with dbus_pending_call_allocate_data_slot(). + * + * @param pending the pending_call + * @param slot the slot number + * @param data the data to store + * @param free_data_func finalizer function for the data + * @returns #TRUE if there was enough memory to store the data + */ +dbus_bool_t +_dbus_pending_call_set_data_unlocked (DBusPendingCall *pending, + dbus_int32_t slot, + void *data, + DBusFreeFunction free_data_func) +{ + DBusFreeFunction old_free_func; + void *old_data; + dbus_bool_t retval; + + retval = _dbus_data_slot_list_set (&slot_allocator, + &pending->slot_list, + slot, data, free_data_func, + &old_free_func, &old_data); + + /* Drop locks to call out to app code */ + CONNECTION_UNLOCK (pending->connection); + + if (retval) + { + if (old_free_func) + (* old_free_func) (old_data); + } + + CONNECTION_LOCK (pending->connection); + + return retval; +} + +/** @} */ + +/** + * @defgroup DBusPendingCall DBusPendingCall + * @ingroup DBus + * @brief Pending reply to a method call message + * + * A DBusPendingCall is an object representing an + * expected reply. A #DBusPendingCall can be created + * when you send a message that should have a reply. + * + * @{ + */ + +/** + * @typedef DBusPendingCall + * + * Opaque data type representing a message pending. + */ + +/** + * Increments the reference count on a pending call. + * + * @param pending the pending call object + * @returns the pending call object + */ +DBusPendingCall * +dbus_pending_call_ref (DBusPendingCall *pending) +{ + _dbus_return_val_if_fail (pending != NULL, NULL); + + /* The connection lock is better than the global + * lock in the atomic increment fallback + */ +#ifdef DBUS_HAVE_ATOMIC_INT + _dbus_atomic_inc (&pending->refcount); +#else + CONNECTION_LOCK (pending->connection); + _dbus_assert (pending->refcount.value > 0); + + pending->refcount.value += 1; + CONNECTION_UNLOCK (pending->connection); +#endif + + return pending; +} + +/** + * Decrements the reference count on a pending call, + * freeing it if the count reaches 0. + * + * @param pending the pending call object + */ +void +dbus_pending_call_unref (DBusPendingCall *pending) +{ + dbus_bool_t last_unref; + + _dbus_return_if_fail (pending != NULL); + + /* More efficient to use the connection lock instead of atomic + * int fallback if we lack atomic int decrement + */ +#ifdef DBUS_HAVE_ATOMIC_INT + last_unref = (_dbus_atomic_dec (&pending->refcount) == 1); +#else + CONNECTION_LOCK (pending->connection); + _dbus_assert (pending->refcount.value > 0); + pending->refcount.value -= 1; + last_unref = pending->refcount.value == 0; + CONNECTION_UNLOCK (pending->connection); +#endif + + if (last_unref) + _dbus_pending_call_last_unref(pending); +} + +/** + * Sets a notification function to be called when the reply is + * received or the pending call times out. + * + * @param pending the pending call + * @param function notifier function + * @param user_data data to pass to notifier function + * @param free_user_data function to free the user data + * @returns #FALSE if not enough memory + */ +dbus_bool_t +dbus_pending_call_set_notify (DBusPendingCall *pending, + DBusPendingCallNotifyFunction function, + void *user_data, + DBusFreeFunction free_user_data) +{ + _dbus_return_val_if_fail (pending != NULL, FALSE); + + CONNECTION_LOCK (pending->connection); + + /* could invoke application code! */ + if (!_dbus_pending_call_set_data_unlocked (pending, notify_user_data_slot, + user_data, free_user_data)) + return FALSE; + + pending->function = function; + + CONNECTION_UNLOCK (pending->connection); + + return TRUE; +} + +/** + * Cancels the pending call, such that any reply or error received + * will just be ignored. Drops the dbus library's internal reference + * to the #DBusPendingCall so will free the call if nobody else is + * holding a reference. However you usually get a reference from + * dbus_connection_send_with_reply() so probably your app owns a ref + * also. + * + * Note that canceling a pending call will not simulate a + * timed-out call; if a call times out, then a timeout error reply is + * received. If you cancel the call, no reply is received unless the + * the reply was already received before you canceled. + * + * @param pending the pending call + */ +void +dbus_pending_call_cancel (DBusPendingCall *pending) +{ + _dbus_return_if_fail (pending != NULL); + + _dbus_connection_remove_pending_call (pending->connection, + pending); +} + +/** + * Checks whether the pending call has received a reply + * yet, or not. + * + * @param pending the pending call + * @returns #TRUE if a reply has been received + */ +dbus_bool_t +dbus_pending_call_get_completed (DBusPendingCall *pending) +{ + dbus_bool_t completed; + + _dbus_return_val_if_fail (pending != NULL, FALSE); + + CONNECTION_LOCK (pending->connection); + completed = pending->completed; + CONNECTION_UNLOCK (pending->connection); + + return completed; +} + +/** + * Gets the reply, or returns #NULL if none has been received + * yet. Ownership of the reply message passes to the caller. This + * function can only be called once per pending call, since the reply + * message is tranferred to the caller. + * + * @param pending the pending call + * @returns the reply message or #NULL. + */ +DBusMessage* +dbus_pending_call_steal_reply (DBusPendingCall *pending) +{ + DBusMessage *message; + + _dbus_return_val_if_fail (pending != NULL, NULL); + _dbus_return_val_if_fail (pending->completed, NULL); + _dbus_return_val_if_fail (pending->reply != NULL, NULL); + + CONNECTION_LOCK (pending->connection); + + message = pending->reply; + pending->reply = NULL; + + CONNECTION_UNLOCK (pending->connection); + + return message; +} + +/** + * Block until the pending call is completed. The blocking is as with + * dbus_connection_send_with_reply_and_block(); it does not enter the + * main loop or process other messages, it simply waits for the reply + * in question. + * + * If the pending call is already completed, this function returns + * immediately. + * + * @todo when you start blocking, the timeout is reset, but it should + * really only use time remaining since the pending call was created. + * This requires storing timestamps instead of intervals in the timeout + * + * @param pending the pending call + */ +void +dbus_pending_call_block (DBusPendingCall *pending) +{ + _dbus_return_if_fail (pending != NULL); + + _dbus_connection_block_pending_call (pending); +} + +/** + * Allocates an integer ID to be used for storing application-specific + * data on any DBusPendingCall. The allocated ID may then be used + * with dbus_pending_call_set_data() and dbus_pending_call_get_data(). + * The passed-in slot must be initialized to -1, and is filled in + * with the slot ID. If the passed-in slot is not -1, it's assumed + * to be already allocated, and its refcount is incremented. + * + * The allocated slot is global, i.e. all DBusPendingCall objects will + * have a slot with the given integer ID reserved. + * + * @param slot_p address of a global variable storing the slot + * @returns #FALSE on failure (no memory) + */ +dbus_bool_t +dbus_pending_call_allocate_data_slot (dbus_int32_t *slot_p) +{ + _dbus_return_val_if_fail (slot_p != NULL, FALSE); + + return _dbus_data_slot_allocator_alloc (&slot_allocator, + &_DBUS_LOCK_NAME (pending_call_slots), + slot_p); +} + +/** + * Deallocates a global ID for #DBusPendingCall data slots. + * dbus_pending_call_get_data() and dbus_pending_call_set_data() may + * no longer be used with this slot. Existing data stored on existing + * DBusPendingCall objects will be freed when the #DBusPendingCall is + * finalized, but may not be retrieved (and may only be replaced if + * someone else reallocates the slot). When the refcount on the + * passed-in slot reaches 0, it is set to -1. + * + * @param slot_p address storing the slot to deallocate + */ +void +dbus_pending_call_free_data_slot (dbus_int32_t *slot_p) +{ + _dbus_return_if_fail (slot_p != NULL); + _dbus_return_if_fail (*slot_p >= 0); + + _dbus_data_slot_allocator_free (&slot_allocator, slot_p); +} + +/** + * Stores a pointer on a #DBusPendingCall, along + * with an optional function to be used for freeing + * the data when the data is set again, or when + * the pending call is finalized. The slot number + * must have been allocated with dbus_pending_call_allocate_data_slot(). + * + * @param pending the pending_call + * @param slot the slot number + * @param data the data to store + * @param free_data_func finalizer function for the data + * @returns #TRUE if there was enough memory to store the data + */ +dbus_bool_t +dbus_pending_call_set_data (DBusPendingCall *pending, + dbus_int32_t slot, + void *data, + DBusFreeFunction free_data_func) +{ + dbus_bool_t retval; + + _dbus_return_val_if_fail (pending != NULL, FALSE); + _dbus_return_val_if_fail (slot >= 0, FALSE); + + + CONNECTION_LOCK (pending->connection); + retval = _dbus_pending_call_set_data_unlocked (pending, slot, data, free_data_func); + CONNECTION_UNLOCK (pending->connection); + return retval; +} + +/** + * Retrieves data previously set with dbus_pending_call_set_data(). + * The slot must still be allocated (must not have been freed). + * + * @param pending the pending_call + * @param slot the slot to get data from + * @returns the data, or #NULL if not found + */ +void* +dbus_pending_call_get_data (DBusPendingCall *pending, + dbus_int32_t slot) +{ + void *res; + + _dbus_return_val_if_fail (pending != NULL, NULL); + + CONNECTION_LOCK (pending->connection); + res = _dbus_data_slot_list_get (&slot_allocator, + &pending->slot_list, + slot); + CONNECTION_UNLOCK (pending->connection); + + return res; +} + +/** @} */ + +#ifdef DBUS_BUILD_TESTS + +/** + * @ingroup DBusPendingCallInternals + * Unit test for DBusPendingCall. + * + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_pending_call_test (const char *test_data_dir) +{ + + return TRUE; +} +#endif /* DBUS_BUILD_TESTS */ diff --git a/src/dbus/dbus-pending-call.h b/src/dbus/dbus-pending-call.h new file mode 100644 index 0000000..b49e08d --- /dev/null +++ b/src/dbus/dbus-pending-call.h @@ -0,0 +1,65 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-pending-call.h Object representing a call in progress. + * + * Copyright (C) 2002, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION) +#error "Only can be included directly, this file may disappear or change contents." +#endif + +#ifndef DBUS_PENDING_CALL_H +#define DBUS_PENDING_CALL_H + +#include +#include +#include + +DBUS_BEGIN_DECLS + +/** + * @addtogroup DBusPendingCall + * @{ + */ + +DBusPendingCall* dbus_pending_call_ref (DBusPendingCall *pending); +void dbus_pending_call_unref (DBusPendingCall *pending); +dbus_bool_t dbus_pending_call_set_notify (DBusPendingCall *pending, + DBusPendingCallNotifyFunction function, + void *user_data, + DBusFreeFunction free_user_data); +void dbus_pending_call_cancel (DBusPendingCall *pending); +dbus_bool_t dbus_pending_call_get_completed (DBusPendingCall *pending); +DBusMessage* dbus_pending_call_steal_reply (DBusPendingCall *pending); +void dbus_pending_call_block (DBusPendingCall *pending); + +dbus_bool_t dbus_pending_call_allocate_data_slot (dbus_int32_t *slot_p); +void dbus_pending_call_free_data_slot (dbus_int32_t *slot_p); +dbus_bool_t dbus_pending_call_set_data (DBusPendingCall *pending, + dbus_int32_t slot, + void *data, + DBusFreeFunction free_data_func); +void* dbus_pending_call_get_data (DBusPendingCall *pending, + dbus_int32_t slot); + +/** @} */ + +DBUS_END_DECLS + +#endif /* DBUS_PENDING_CALL_H */ diff --git a/src/dbus/dbus-protocol.h b/src/dbus/dbus-protocol.h new file mode 100644 index 0000000..814deae --- /dev/null +++ b/src/dbus/dbus-protocol.h @@ -0,0 +1,439 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-protocol.h D-Bus protocol constants + * + * Copyright (C) 2002, 2003 CodeFactory AB + * Copyright (C) 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef DBUS_PROTOCOL_H +#define DBUS_PROTOCOL_H + +/* Don't include anything in here from anywhere else. It's + * intended for use by any random library. + */ + +#ifdef __cplusplus +extern "C" { +#if 0 +} /* avoids confusing emacs indentation */ +#endif +#endif + +/* Normally docs are in .c files, but there isn't a .c file for this. */ +/** + * @defgroup DBusProtocol Protocol constants + * @ingroup DBus + * + * @brief Defines constants which are part of the D-Bus protocol + * + * This header is intended for use by any library, not only libdbus. + * + * @{ + */ + + +/* Message byte order */ +#define DBUS_LITTLE_ENDIAN ('l') /**< Code marking LSB-first byte order in the wire protocol. */ +#define DBUS_BIG_ENDIAN ('B') /**< Code marking MSB-first byte order in the wire protocol. */ + +/** Protocol version. */ +#define DBUS_MAJOR_PROTOCOL_VERSION 1 + +/** Type code that is never equal to a legitimate type code */ +#define DBUS_TYPE_INVALID ((int) '\0') +/** #DBUS_TYPE_INVALID as a string literal instead of a int literal */ +#define DBUS_TYPE_INVALID_AS_STRING "\0" + +/* Primitive types */ +/** Type code marking an 8-bit unsigned integer */ +#define DBUS_TYPE_BYTE ((int) 'y') +/** #DBUS_TYPE_BYTE as a string literal instead of a int literal */ +#define DBUS_TYPE_BYTE_AS_STRING "y" +/** Type code marking a boolean */ +#define DBUS_TYPE_BOOLEAN ((int) 'b') +/** #DBUS_TYPE_BOOLEAN as a string literal instead of a int literal */ +#define DBUS_TYPE_BOOLEAN_AS_STRING "b" +/** Type code marking a 16-bit signed integer */ +#define DBUS_TYPE_INT16 ((int) 'n') +/** #DBUS_TYPE_INT16 as a string literal instead of a int literal */ +#define DBUS_TYPE_INT16_AS_STRING "n" +/** Type code marking a 16-bit unsigned integer */ +#define DBUS_TYPE_UINT16 ((int) 'q') +/** #DBUS_TYPE_UINT16 as a string literal instead of a int literal */ +#define DBUS_TYPE_UINT16_AS_STRING "q" +/** Type code marking a 32-bit signed integer */ +#define DBUS_TYPE_INT32 ((int) 'i') +/** #DBUS_TYPE_INT32 as a string literal instead of a int literal */ +#define DBUS_TYPE_INT32_AS_STRING "i" +/** Type code marking a 32-bit unsigned integer */ +#define DBUS_TYPE_UINT32 ((int) 'u') +/** #DBUS_TYPE_UINT32 as a string literal instead of a int literal */ +#define DBUS_TYPE_UINT32_AS_STRING "u" +/** Type code marking a 64-bit signed integer */ +#define DBUS_TYPE_INT64 ((int) 'x') +/** #DBUS_TYPE_INT64 as a string literal instead of a int literal */ +#define DBUS_TYPE_INT64_AS_STRING "x" +/** Type code marking a 64-bit unsigned integer */ +#define DBUS_TYPE_UINT64 ((int) 't') +/** #DBUS_TYPE_UINT64 as a string literal instead of a int literal */ +#define DBUS_TYPE_UINT64_AS_STRING "t" +/** Type code marking an 8-byte double in IEEE 754 format */ +#define DBUS_TYPE_DOUBLE ((int) 'd') +/** #DBUS_TYPE_DOUBLE as a string literal instead of a int literal */ +#define DBUS_TYPE_DOUBLE_AS_STRING "d" +/** Type code marking a UTF-8 encoded, nul-terminated Unicode string */ +#define DBUS_TYPE_STRING ((int) 's') +/** #DBUS_TYPE_STRING as a string literal instead of a int literal */ +#define DBUS_TYPE_STRING_AS_STRING "s" +/** Type code marking a D-Bus object path */ +#define DBUS_TYPE_OBJECT_PATH ((int) 'o') +/** #DBUS_TYPE_OBJECT_PATH as a string literal instead of a int literal */ +#define DBUS_TYPE_OBJECT_PATH_AS_STRING "o" +/** Type code marking a D-Bus type signature */ +#define DBUS_TYPE_SIGNATURE ((int) 'g') +/** #DBUS_TYPE_SIGNATURE as a string literal instead of a int literal */ +#define DBUS_TYPE_SIGNATURE_AS_STRING "g" + +/* Compound types */ +/** Type code marking a D-Bus array type */ +#define DBUS_TYPE_ARRAY ((int) 'a') +/** #DBUS_TYPE_ARRAY as a string literal instead of a int literal */ +#define DBUS_TYPE_ARRAY_AS_STRING "a" +/** Type code marking a D-Bus variant type */ +#define DBUS_TYPE_VARIANT ((int) 'v') +/** #DBUS_TYPE_VARIANT as a string literal instead of a int literal */ +#define DBUS_TYPE_VARIANT_AS_STRING "v" + +/** STRUCT and DICT_ENTRY are sort of special since their codes can't + * appear in a type string, instead + * DBUS_STRUCT_BEGIN_CHAR/DBUS_DICT_ENTRY_BEGIN_CHAR have to appear + */ +/** Type code used to represent a struct; however, this type code does not appear + * in type signatures, instead #DBUS_STRUCT_BEGIN_CHAR and #DBUS_STRUCT_END_CHAR will + * appear in a signature. + */ +#define DBUS_TYPE_STRUCT ((int) 'r') +/** #DBUS_TYPE_STRUCT as a string literal instead of a int literal */ +#define DBUS_TYPE_STRUCT_AS_STRING "r" +/** Type code used to represent a dict entry; however, this type code does not appear + * in type signatures, instead #DBUS_DICT_ENTRY_BEGIN_CHAR and #DBUS_DICT_ENTRY_END_CHAR will + * appear in a signature. + */ +#define DBUS_TYPE_DICT_ENTRY ((int) 'e') +/** #DBUS_TYPE_DICT_ENTRY as a string literal instead of a int literal */ +#define DBUS_TYPE_DICT_ENTRY_AS_STRING "e" + +/** Does not include #DBUS_TYPE_INVALID, #DBUS_STRUCT_BEGIN_CHAR, #DBUS_STRUCT_END_CHAR, + * #DBUS_DICT_ENTRY_BEGIN_CHAR, or #DBUS_DICT_ENTRY_END_CHAR - i.e. it is the number of + * valid types, not the number of distinct characters that may appear in a type signature. + */ +#define DBUS_NUMBER_OF_TYPES (16) + +/* characters other than typecodes that appear in type signatures */ + +/** Code marking the start of a struct type in a type signature */ +#define DBUS_STRUCT_BEGIN_CHAR ((int) '(') +/** #DBUS_STRUCT_BEGIN_CHAR as a string literal instead of a int literal */ +#define DBUS_STRUCT_BEGIN_CHAR_AS_STRING "(" +/** Code marking the end of a struct type in a type signature */ +#define DBUS_STRUCT_END_CHAR ((int) ')') +/** #DBUS_STRUCT_END_CHAR a string literal instead of a int literal */ +#define DBUS_STRUCT_END_CHAR_AS_STRING ")" +/** Code marking the start of a dict entry type in a type signature */ +#define DBUS_DICT_ENTRY_BEGIN_CHAR ((int) '{') +/** #DBUS_DICT_ENTRY_BEGIN_CHAR as a string literal instead of a int literal */ +#define DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING "{" +/** Code marking the end of a dict entry type in a type signature */ +#define DBUS_DICT_ENTRY_END_CHAR ((int) '}') +/** #DBUS_DICT_ENTRY_END_CHAR as a string literal instead of a int literal */ +#define DBUS_DICT_ENTRY_END_CHAR_AS_STRING "}" + +/** Max length in bytes of a bus name, interface, or member (not object + * path, paths are unlimited). This is limited because lots of stuff + * is O(n) in this number, plus it would be obnoxious to type in a + * paragraph-long method name so most likely something like that would + * be an exploit. + */ +#define DBUS_MAXIMUM_NAME_LENGTH 255 + +/** This one is 255 so it fits in a byte */ +#define DBUS_MAXIMUM_SIGNATURE_LENGTH 255 + +/** Max length of a match rule string; to keep people from hosing the + * daemon with some huge rule + */ +#define DBUS_MAXIMUM_MATCH_RULE_LENGTH 1024 + +/** Max arg number you can match on in a match rule, e.g. + * arg0='hello' is OK, arg3489720987='hello' is not + */ +#define DBUS_MAXIMUM_MATCH_RULE_ARG_NUMBER 63 + +/** Max length of a marshaled array in bytes (64M, 2^26) We use signed + * int for lengths so must be INT_MAX or less. We need something a + * bit smaller than INT_MAX because the array is inside a message with + * header info, etc. so an INT_MAX array wouldn't allow the message + * overhead. The 64M number is an attempt at a larger number than + * we'd reasonably ever use, but small enough that your bus would chew + * through it fairly quickly without locking up forever. If you have + * data that's likely to be larger than this, you should probably be + * sending it in multiple incremental messages anyhow. + */ +#define DBUS_MAXIMUM_ARRAY_LENGTH (67108864) +/** Number of bits you need in an unsigned to store the max array size */ +#define DBUS_MAXIMUM_ARRAY_LENGTH_BITS 26 + +/** The maximum total message size including header and body; similar + * rationale to max array size. + */ +#define DBUS_MAXIMUM_MESSAGE_LENGTH (DBUS_MAXIMUM_ARRAY_LENGTH * 2) +/** Number of bits you need in an unsigned to store the max message size */ +#define DBUS_MAXIMUM_MESSAGE_LENGTH_BITS 27 + +/** Depth of recursion in the type tree. This is automatically limited + * to DBUS_MAXIMUM_SIGNATURE_LENGTH since you could only have an array + * of array of array of ... that fit in the max signature. But that's + * probably a bit too large. + */ +#define DBUS_MAXIMUM_TYPE_RECURSION_DEPTH 32 + +/* Types of message */ + +/** This value is never a valid message type, see dbus_message_get_type() */ +#define DBUS_MESSAGE_TYPE_INVALID 0 +/** Message type of a method call message, see dbus_message_get_type() */ +#define DBUS_MESSAGE_TYPE_METHOD_CALL 1 +/** Message type of a method return message, see dbus_message_get_type() */ +#define DBUS_MESSAGE_TYPE_METHOD_RETURN 2 +/** Message type of an error reply message, see dbus_message_get_type() */ +#define DBUS_MESSAGE_TYPE_ERROR 3 +/** Message type of a signal message, see dbus_message_get_type() */ +#define DBUS_MESSAGE_TYPE_SIGNAL 4 + +/* Header flags */ + +/** If set, this flag means that the sender of a message does not care about getting + * a reply, so the recipient need not send one. See dbus_message_set_no_reply(). + */ +#define DBUS_HEADER_FLAG_NO_REPLY_EXPECTED 0x1 +/** + * If set, this flag means that even if the message bus knows how to start an owner for + * the destination bus name (see dbus_message_set_destination()), it should not + * do so. If this flag is not set, the bus may launch a program to process the + * message. + */ +#define DBUS_HEADER_FLAG_NO_AUTO_START 0x2 + +/* Header fields */ + +/** Not equal to any valid header field code */ +#define DBUS_HEADER_FIELD_INVALID 0 +/** Header field code for the path - the path is the object emitting a signal or the object receiving a method call. + * See dbus_message_set_path(). + */ +#define DBUS_HEADER_FIELD_PATH 1 +/** Header field code for the interface containing a member (method or signal). + * See dbus_message_set_interface(). + */ +#define DBUS_HEADER_FIELD_INTERFACE 2 +/** Header field code for a member (method or signal). See dbus_message_set_member(). */ +#define DBUS_HEADER_FIELD_MEMBER 3 +/** Header field code for an error name (found in #DBUS_MESSAGE_TYPE_ERROR messages). + * See dbus_message_set_error_name(). + */ +#define DBUS_HEADER_FIELD_ERROR_NAME 4 +/** Header field code for a reply serial, used to match a #DBUS_MESSAGE_TYPE_METHOD_RETURN message with the + * message that it's a reply to. See dbus_message_set_reply_serial(). + */ +#define DBUS_HEADER_FIELD_REPLY_SERIAL 5 +/** + * Header field code for the destination bus name of a message. See dbus_message_set_destination(). + */ +#define DBUS_HEADER_FIELD_DESTINATION 6 +/** + * Header field code for the sender of a message; usually initialized by the message bus. + * See dbus_message_set_sender(). + */ +#define DBUS_HEADER_FIELD_SENDER 7 +/** + * Header field code for the type signature of a message. + */ +#define DBUS_HEADER_FIELD_SIGNATURE 8 + +/** + * Value of the highest-numbered header field code, can be used to determine + * the size of an array indexed by header field code. Remember though + * that unknown codes must be ignored, so check for that before + * indexing the array. + */ +#define DBUS_HEADER_FIELD_LAST DBUS_HEADER_FIELD_SIGNATURE + +/** Header format is defined as a signature: + * byte byte order + * byte message type ID + * byte flags + * byte protocol version + * uint32 body length + * uint32 serial + * array of struct (byte,variant) (field name, value) + * + * The length of the header can be computed as the + * fixed size of the initial data, plus the length of + * the array at the end, plus padding to an 8-boundary. + */ +#define DBUS_HEADER_SIGNATURE \ + DBUS_TYPE_BYTE_AS_STRING \ + DBUS_TYPE_BYTE_AS_STRING \ + DBUS_TYPE_BYTE_AS_STRING \ + DBUS_TYPE_BYTE_AS_STRING \ + DBUS_TYPE_UINT32_AS_STRING \ + DBUS_TYPE_UINT32_AS_STRING \ + DBUS_TYPE_ARRAY_AS_STRING \ + DBUS_STRUCT_BEGIN_CHAR_AS_STRING \ + DBUS_TYPE_BYTE_AS_STRING \ + DBUS_TYPE_VARIANT_AS_STRING \ + DBUS_STRUCT_END_CHAR_AS_STRING + + +/** + * The smallest header size that can occur. (It won't be valid due to + * missing required header fields.) This is 4 bytes, two uint32, an + * array length. This isn't any kind of resource limit, just the + * necessary/logical outcome of the header signature. + */ +#define DBUS_MINIMUM_HEADER_SIZE 16 + +/* Errors */ +/* WARNING these get autoconverted to an enum in dbus-glib.h. Thus, + * if you change the order it breaks the ABI. Keep them in order. + * Also, don't change the formatting since that will break the sed + * script. + */ +/** A generic error; "something went wrong" - see the error message for more. */ +#define DBUS_ERROR_FAILED "org.freedesktop.DBus.Error.Failed" +/** There was not enough memory to complete an operation. */ +#define DBUS_ERROR_NO_MEMORY "org.freedesktop.DBus.Error.NoMemory" +/** The bus doesn't know how to launch a service to supply the bus name you wanted. */ +#define DBUS_ERROR_SERVICE_UNKNOWN "org.freedesktop.DBus.Error.ServiceUnknown" +/** The bus name you referenced doesn't exist (i.e. no application owns it). */ +#define DBUS_ERROR_NAME_HAS_NO_OWNER "org.freedesktop.DBus.Error.NameHasNoOwner" +/** No reply to a message expecting one, usually means a timeout occurred. */ +#define DBUS_ERROR_NO_REPLY "org.freedesktop.DBus.Error.NoReply" +/** Something went wrong reading or writing to a socket, for example. */ +#define DBUS_ERROR_IO_ERROR "org.freedesktop.DBus.Error.IOError" +/** A D-Bus bus address was malformed. */ +#define DBUS_ERROR_BAD_ADDRESS "org.freedesktop.DBus.Error.BadAddress" +/** Requested operation isn't supported (like ENOSYS on UNIX). */ +#define DBUS_ERROR_NOT_SUPPORTED "org.freedesktop.DBus.Error.NotSupported" +/** Some limited resource is exhausted. */ +#define DBUS_ERROR_LIMITS_EXCEEDED "org.freedesktop.DBus.Error.LimitsExceeded" +/** Security restrictions don't allow doing what you're trying to do. */ +#define DBUS_ERROR_ACCESS_DENIED "org.freedesktop.DBus.Error.AccessDenied" +/** Authentication didn't work. */ +#define DBUS_ERROR_AUTH_FAILED "org.freedesktop.DBus.Error.AuthFailed" +/** Unable to connect to server (probably caused by ECONNREFUSED on a socket). */ +#define DBUS_ERROR_NO_SERVER "org.freedesktop.DBus.Error.NoServer" +/** Certain timeout errors, possibly ETIMEDOUT on a socket. + * Note that #DBUS_ERROR_NO_REPLY is used for message reply timeouts. + * @warning this is confusingly-named given that #DBUS_ERROR_TIMED_OUT also exists. We can't fix + * it for compatibility reasons so just be careful. + */ +#define DBUS_ERROR_TIMEOUT "org.freedesktop.DBus.Error.Timeout" +/** No network access (probably ENETUNREACH on a socket). */ +#define DBUS_ERROR_NO_NETWORK "org.freedesktop.DBus.Error.NoNetwork" +/** Can't bind a socket since its address is in use (i.e. EADDRINUSE). */ +#define DBUS_ERROR_ADDRESS_IN_USE "org.freedesktop.DBus.Error.AddressInUse" +/** The connection is disconnected and you're trying to use it. */ +#define DBUS_ERROR_DISCONNECTED "org.freedesktop.DBus.Error.Disconnected" +/** Invalid arguments passed to a method call. */ +#define DBUS_ERROR_INVALID_ARGS "org.freedesktop.DBus.Error.InvalidArgs" +/** Missing file. */ +#define DBUS_ERROR_FILE_NOT_FOUND "org.freedesktop.DBus.Error.FileNotFound" +/** Existing file and the operation you're using does not silently overwrite. */ +#define DBUS_ERROR_FILE_EXISTS "org.freedesktop.DBus.Error.FileExists" +/** Method name you invoked isn't known by the object you invoked it on. */ +#define DBUS_ERROR_UNKNOWN_METHOD "org.freedesktop.DBus.Error.UnknownMethod" +/** Certain timeout errors, e.g. while starting a service. + * @warning this is confusingly-named given that #DBUS_ERROR_TIMEOUT also exists. We can't fix + * it for compatibility reasons so just be careful. + */ +#define DBUS_ERROR_TIMED_OUT "org.freedesktop.DBus.Error.TimedOut" +/** Tried to remove or modify a match rule that didn't exist. */ +#define DBUS_ERROR_MATCH_RULE_NOT_FOUND "org.freedesktop.DBus.Error.MatchRuleNotFound" +/** The match rule isn't syntactically valid. */ +#define DBUS_ERROR_MATCH_RULE_INVALID "org.freedesktop.DBus.Error.MatchRuleInvalid" +/** While starting a new process, the exec() call failed. */ +#define DBUS_ERROR_SPAWN_EXEC_FAILED "org.freedesktop.DBus.Error.Spawn.ExecFailed" +/** While starting a new process, the fork() call failed. */ +#define DBUS_ERROR_SPAWN_FORK_FAILED "org.freedesktop.DBus.Error.Spawn.ForkFailed" +/** While starting a new process, the child exited with a status code. */ +#define DBUS_ERROR_SPAWN_CHILD_EXITED "org.freedesktop.DBus.Error.Spawn.ChildExited" +/** While starting a new process, the child exited on a signal. */ +#define DBUS_ERROR_SPAWN_CHILD_SIGNALED "org.freedesktop.DBus.Error.Spawn.ChildSignaled" +/** While starting a new process, something went wrong. */ +#define DBUS_ERROR_SPAWN_FAILED "org.freedesktop.DBus.Error.Spawn.Failed" +/** We failed to setup the environment correctly. */ +#define DBUS_ERROR_SPAWN_SETUP_FAILED "org.freedesktop.DBus.Error.Spawn.FailedToSetup" +/** We failed to setup the config parser correctly. */ +#define DBUS_ERROR_SPAWN_CONFIG_INVALID "org.freedesktop.DBus.Error.Spawn.ConfigInvalid" +/** Bus name was not valid. */ +#define DBUS_ERROR_SPAWN_SERVICE_INVALID "org.freedesktop.DBus.Error.Spawn.ServiceNotValid" +/** Service file not found in system-services directory. */ +#define DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND "org.freedesktop.DBus.Error.Spawn.ServiceNotFound" +/** Permissions are incorrect on the setuid helper. */ +#define DBUS_ERROR_SPAWN_PERMISSIONS_INVALID "org.freedesktop.DBus.Error.Spawn.PermissionsInvalid" +/** Service file invalid (Name, User or Exec missing). */ +#define DBUS_ERROR_SPAWN_FILE_INVALID "org.freedesktop.DBus.Error.Spawn.FileInvalid" +/** Tried to get a UNIX process ID and it wasn't available. */ +#define DBUS_ERROR_SPAWN_NO_MEMORY "org.freedesktop.DBus.Error.Spawn.NoMemory" +/** Tried to get a UNIX process ID and it wasn't available. */ +#define DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN "org.freedesktop.DBus.Error.UnixProcessIdUnknown" +/** A type signature is not valid. */ +#define DBUS_ERROR_INVALID_SIGNATURE "org.freedesktop.DBus.Error.InvalidSignature" +/** A file contains invalid syntax or is otherwise broken. */ +#define DBUS_ERROR_INVALID_FILE_CONTENT "org.freedesktop.DBus.Error.InvalidFileContent" +/** Asked for SELinux security context and it wasn't available. */ +#define DBUS_ERROR_SELINUX_SECURITY_CONTEXT_UNKNOWN "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown" +/** Asked for ADT audit data and it wasn't available. */ +#define DBUS_ERROR_ADT_AUDIT_DATA_UNKNOWN "org.freedesktop.DBus.Error.AdtAuditDataUnknown" +/** There's already an object with the requested object path. */ +#define DBUS_ERROR_OBJECT_PATH_IN_USE "org.freedesktop.DBus.Error.ObjectPathInUse" + +/* XML introspection format */ + +/** XML namespace of the introspection format version 1.0 */ +#define DBUS_INTROSPECT_1_0_XML_NAMESPACE "http://www.freedesktop.org/standards/dbus" +/** XML public identifier of the introspection format version 1.0 */ +#define DBUS_INTROSPECT_1_0_XML_PUBLIC_IDENTIFIER "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" +/** XML system identifier of the introspection format version 1.0 */ +#define DBUS_INTROSPECT_1_0_XML_SYSTEM_IDENTIFIER "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd" +/** XML document type declaration of the introspection format version 1.0 */ +#define DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE "\n" + +/** @} */ + +#ifdef __cplusplus +#if 0 +{ /* avoids confusing emacs indentation */ +#endif +} +#endif + +#endif /* DBUS_PROTOCOL_H */ diff --git a/src/dbus/dbus-resources.c b/src/dbus/dbus-resources.c new file mode 100644 index 0000000..5ff1622 --- /dev/null +++ b/src/dbus/dbus-resources.c @@ -0,0 +1,194 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-resources.c Resource tracking/limits + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include +#include + +/** + * @defgroup DBusResources Resource limits related code + * @ingroup DBusInternals + * @brief DBusCounter and other stuff related to resource limits + * + * Types and functions related to tracking resource limits, + * such as the maximum amount of memory a connection can use + * for messages, etc. + */ + +/** + * @defgroup DBusResourcesInternals Resource limits implementation details + * @ingroup DBusInternals + * @brief Resource limits implementation details + * + * Implementation details of resource limits code. + * + * @{ + */ + +/** + * @brief Internals of DBusCounter. + * + * DBusCounter internals. DBusCounter is an opaque object, it must be + * used via accessor functions. + */ +struct DBusCounter +{ + int refcount; /**< reference count */ + + long value; /**< current counter value */ + + long notify_guard_value; /**< call notify function when crossing this value */ + DBusCounterNotifyFunction notify_function; /**< notify function */ + void *notify_data; /**< data for notify function */ +}; + +/** @} */ /* end of resource limits internals docs */ + +/** + * @addtogroup DBusResources + * @{ + */ + +/** + * Creates a new DBusCounter. DBusCounter is used + * to count usage of some resource such as memory. + * + * @returns new counter or #NULL on failure + */ +DBusCounter* +_dbus_counter_new (void) +{ + DBusCounter *counter; + + counter = dbus_new (DBusCounter, 1); + if (counter == NULL) + return NULL; + + counter->refcount = 1; + counter->value = 0; + + counter->notify_guard_value = 0; + counter->notify_function = NULL; + counter->notify_data = NULL; + + return counter; +} + +/** + * Increments refcount of the counter + * + * @param counter the counter + * @returns the counter + */ +DBusCounter * +_dbus_counter_ref (DBusCounter *counter) +{ + _dbus_assert (counter->refcount > 0); + + counter->refcount += 1; + + return counter; +} + +/** + * Decrements refcount of the counter and possibly + * finalizes the counter. + * + * @param counter the counter + */ +void +_dbus_counter_unref (DBusCounter *counter) +{ + _dbus_assert (counter->refcount > 0); + + counter->refcount -= 1; + + if (counter->refcount == 0) + { + + dbus_free (counter); + } +} + +/** + * Adjusts the value of the counter by the given + * delta which may be positive or negative. + * Calls the notify function from _dbus_counter_set_notify() + * if that function has been specified. + * + * @param counter the counter + * @param delta value to add to the counter's current value + */ +void +_dbus_counter_adjust (DBusCounter *counter, + long delta) +{ + long old = counter->value; + + counter->value += delta; + +#if 0 + _dbus_verbose ("Adjusting counter %ld by %ld = %ld\n", + old, delta, counter->value); +#endif + + if (counter->notify_function != NULL && + ((old < counter->notify_guard_value && + counter->value >= counter->notify_guard_value) || + (old >= counter->notify_guard_value && + counter->value < counter->notify_guard_value))) + (* counter->notify_function) (counter, counter->notify_data); +} + +/** + * Gets the current value of the counter. + * + * @param counter the counter + * @returns its current value + */ +long +_dbus_counter_get_value (DBusCounter *counter) +{ + return counter->value; +} + +/** + * Sets the notify function for this counter; the notify function is + * called whenever the counter's value crosses the guard value in + * either direction (moving up, or moving down). + * + * @param counter the counter + * @param guard_value the value we're notified if the counter crosses + * @param function function to call in order to notify + * @param user_data data to pass to the function + */ +void +_dbus_counter_set_notify (DBusCounter *counter, + long guard_value, + DBusCounterNotifyFunction function, + void *user_data) +{ + counter->notify_guard_value = guard_value; + counter->notify_function = function; + counter->notify_data = user_data; +} + +/** @} */ /* end of resource limits exported API */ diff --git a/src/dbus/dbus-resources.h b/src/dbus/dbus-resources.h new file mode 100644 index 0000000..7b6e0d4 --- /dev/null +++ b/src/dbus/dbus-resources.h @@ -0,0 +1,52 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-resources.h Resource tracking/limits + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef DBUS_RESOURCES_H +#define DBUS_RESOURCES_H + +#include +#include +#include + +DBUS_BEGIN_DECLS + +typedef struct DBusCounter DBusCounter; + +typedef void (* DBusCounterNotifyFunction) (DBusCounter *counter, + void *user_data); + +DBusCounter* _dbus_counter_new (void); +DBusCounter* _dbus_counter_ref (DBusCounter *counter); +void _dbus_counter_unref (DBusCounter *counter); +void _dbus_counter_adjust (DBusCounter *counter, + long delta); +long _dbus_counter_get_value (DBusCounter *counter); + +void _dbus_counter_set_notify (DBusCounter *counter, + long guard_value, + DBusCounterNotifyFunction function, + void *user_data); + + +DBUS_END_DECLS + +#endif /* DBUS_RESOURCES_H */ diff --git a/src/dbus/dbus-server-debug-pipe.c b/src/dbus/dbus-server-debug-pipe.c new file mode 100644 index 0000000..24b0ce3 --- /dev/null +++ b/src/dbus/dbus-server-debug-pipe.c @@ -0,0 +1,432 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-server-debug-pipe.c In-proc debug server implementation + * + * Copyright (C) 2003 CodeFactory AB + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-internals.h" +#include "dbus-server-debug-pipe.h" +#include "dbus-transport-socket.h" +#include "dbus-connection-internal.h" +#include "dbus-hash.h" +#include "dbus-string.h" +#include "dbus-protocol.h" + +#ifdef DBUS_BUILD_TESTS + +/** + * @defgroup DBusServerDebugPipe DBusServerDebugPipe + * @ingroup DBusInternals + * @brief In-process pipe debug server used in unit tests. + * + * Types and functions related to DBusServerDebugPipe. + * This is used for unit testing. + * + * @{ + */ + +/** + * Opaque object representing a debug server implementation. + */ +typedef struct DBusServerDebugPipe DBusServerDebugPipe; + +/** + * Implementation details of DBusServerDebugPipe. All members + * are private. + */ +struct DBusServerDebugPipe +{ + DBusServer base; /**< Parent class members. */ + + char *name; /**< Server name. */ + + dbus_bool_t disconnected; /**< TRUE if disconnect has been called */ +}; + +/* FIXME not threadsafe (right now the test suite doesn't use threads anyhow ) */ +static DBusHashTable *server_pipe_hash; +static int server_pipe_hash_refcount = 0; + +static dbus_bool_t +pipe_hash_ref (void) +{ + if (!server_pipe_hash) + { + _dbus_assert (server_pipe_hash_refcount == 0); + + server_pipe_hash = _dbus_hash_table_new (DBUS_HASH_STRING, NULL, NULL); + + if (!server_pipe_hash) + return FALSE; + } + + server_pipe_hash_refcount = 1; + + return TRUE; +} + +static void +pipe_hash_unref (void) +{ + _dbus_assert (server_pipe_hash != NULL); + _dbus_assert (server_pipe_hash_refcount > 0); + + server_pipe_hash_refcount -= 1; + if (server_pipe_hash_refcount == 0) + { + _dbus_hash_table_unref (server_pipe_hash); + server_pipe_hash = NULL; + } +} + +static void +debug_finalize (DBusServer *server) +{ + DBusServerDebugPipe *debug_server = (DBusServerDebugPipe*) server; + + pipe_hash_unref (); + + _dbus_server_finalize_base (server); + + dbus_free (debug_server->name); + dbus_free (server); +} + +static void +debug_disconnect (DBusServer *server) +{ + ((DBusServerDebugPipe*)server)->disconnected = TRUE; +} + +static DBusServerVTable debug_vtable = { + debug_finalize, + debug_disconnect +}; + +/** + * Creates a new debug server using an in-process pipe + * + * @param server_name the name of the server. + * @param error address where an error can be returned. + * @returns a new server, or #NULL on failure. + */ +DBusServer* +_dbus_server_debug_pipe_new (const char *server_name, + DBusError *error) +{ + DBusServerDebugPipe *debug_server; + DBusString address; + DBusString name_str; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + if (!pipe_hash_ref ()) + return NULL; + + if (_dbus_hash_table_lookup_string (server_pipe_hash, server_name) != NULL) + { + dbus_set_error (error, DBUS_ERROR_ADDRESS_IN_USE, NULL); + pipe_hash_unref (); + return NULL; + } + + debug_server = dbus_new0 (DBusServerDebugPipe, 1); + if (debug_server == NULL) + goto nomem_0; + + if (!_dbus_string_init (&address)) + goto nomem_1; + + _dbus_string_init_const (&name_str, server_name); + if (!_dbus_string_append (&address, "debug-pipe:name=") || + !_dbus_address_append_escaped (&address, &name_str)) + goto nomem_2; + + debug_server->name = _dbus_strdup (server_name); + if (debug_server->name == NULL) + goto nomem_2; + + if (!_dbus_server_init_base (&debug_server->base, + &debug_vtable, &address)) + goto nomem_3; + + if (!_dbus_hash_table_insert_string (server_pipe_hash, + debug_server->name, + debug_server)) + goto nomem_4; + + _dbus_string_free (&address); + + /* server keeps the pipe hash ref */ + + return (DBusServer *)debug_server; + + nomem_4: + _dbus_server_finalize_base (&debug_server->base); + nomem_3: + dbus_free (debug_server->name); + nomem_2: + _dbus_string_free (&address); + nomem_1: + dbus_free (debug_server); + nomem_0: + pipe_hash_unref (); + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return NULL; +} + +/** + * Creates the client-side transport for + * a debug-pipe connection connected to the + * given debug-pipe server name. + * + * @param server_name name of server to connect to + * @param error address where an error can be returned. + * @returns #NULL on no memory or transport + */ +DBusTransport* +_dbus_transport_debug_pipe_new (const char *server_name, + DBusError *error) +{ + DBusTransport *client_transport; + DBusTransport *server_transport; + DBusConnection *connection; + int client_fd, server_fd; + DBusServer *server; + DBusString address; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + if (server_pipe_hash == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_SERVER, NULL); + return NULL; + } + + server = _dbus_hash_table_lookup_string (server_pipe_hash, + server_name); + if (server == NULL || + ((DBusServerDebugPipe*)server)->disconnected) + { + dbus_set_error (error, DBUS_ERROR_NO_SERVER, NULL); + return NULL; + } + + if (!_dbus_string_init (&address)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return NULL; + } + + if (!_dbus_string_append (&address, "debug-pipe:name=") || + !_dbus_string_append (&address, server_name)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + _dbus_string_free (&address); + return NULL; + } + + if (!_dbus_full_duplex_pipe (&client_fd, &server_fd, FALSE, + NULL)) + { + _dbus_verbose ("failed to create full duplex pipe\n"); + dbus_set_error (error, DBUS_ERROR_FAILED, "Could not create full-duplex pipe"); + _dbus_string_free (&address); + return NULL; + } + + _dbus_fd_set_close_on_exec (client_fd); + _dbus_fd_set_close_on_exec (server_fd); + + client_transport = _dbus_transport_new_for_socket (client_fd, + NULL, &address); + if (client_transport == NULL) + { + _dbus_close_socket (client_fd, NULL); + _dbus_close_socket (server_fd, NULL); + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + _dbus_string_free (&address); + return NULL; + } + + _dbus_string_free (&address); + + client_fd = -1; + + server_transport = _dbus_transport_new_for_socket (server_fd, + &server->guid_hex, NULL); + if (server_transport == NULL) + { + _dbus_transport_unref (client_transport); + _dbus_close_socket (server_fd, NULL); + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return NULL; + } + + server_fd = -1; + + if (!_dbus_transport_set_auth_mechanisms (server_transport, + (const char**) server->auth_mechanisms)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + _dbus_transport_unref (server_transport); + _dbus_transport_unref (client_transport); + return NULL; + } + + connection = _dbus_connection_new_for_transport (server_transport); + _dbus_transport_unref (server_transport); + server_transport = NULL; + + if (connection == NULL) + { + _dbus_transport_unref (client_transport); + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return NULL; + } + + /* See if someone wants to handle this new connection, + * self-referencing for paranoia + */ + if (server->new_connection_function) + { + dbus_server_ref (server); + (* server->new_connection_function) (server, connection, + server->new_connection_data); + dbus_server_unref (server); + } + + /* If no one grabbed a reference, the connection will die, + * and the client transport will get an immediate disconnect + */ + _dbus_connection_close_if_only_one_ref (connection); + dbus_connection_unref (connection); + + return client_transport; +} + +/** + * Tries to interpret the address entry as a debug pipe entry. + * + * Sets error if the result is not OK. + * + * @param entry an address entry + * @param server_p location to store a new DBusServer, or #NULL on failure. + * @param error location to store rationale for failure on bad address + * @returns the outcome + * + */ +DBusServerListenResult +_dbus_server_listen_debug_pipe (DBusAddressEntry *entry, + DBusServer **server_p, + DBusError *error) +{ + const char *method; + + *server_p = NULL; + + method = dbus_address_entry_get_method (entry); + + if (strcmp (method, "debug-pipe") == 0) + { + const char *name = dbus_address_entry_get_value (entry, "name"); + + if (name == NULL) + { + _dbus_set_bad_address(error, "debug-pipe", "name", + NULL); + return DBUS_SERVER_LISTEN_BAD_ADDRESS; + } + + *server_p = _dbus_server_debug_pipe_new (name, error); + + if (*server_p) + { + _DBUS_ASSERT_ERROR_IS_CLEAR(error); + return DBUS_SERVER_LISTEN_OK; + } + else + { + _DBUS_ASSERT_ERROR_IS_SET(error); + return DBUS_SERVER_LISTEN_DID_NOT_CONNECT; + } + } + else + { + _DBUS_ASSERT_ERROR_IS_CLEAR(error); + return DBUS_SERVER_LISTEN_NOT_HANDLED; + } +} + +/** + * Opens a debug pipe transport, used in the test suite. + * + * @param entry the address entry to try opening as debug-pipe + * @param transport_p return location for the opened transport + * @param error error to be set + * @returns result of the attempt + */ +DBusTransportOpenResult +_dbus_transport_open_debug_pipe (DBusAddressEntry *entry, + DBusTransport **transport_p, + DBusError *error) +{ + const char *method; + + method = dbus_address_entry_get_method (entry); + _dbus_assert (method != NULL); + + if (strcmp (method, "debug-pipe") == 0) + { + const char *name = dbus_address_entry_get_value (entry, "name"); + + if (name == NULL) + { + _dbus_set_bad_address (error, "debug-pipe", "name", + NULL); + return DBUS_TRANSPORT_OPEN_BAD_ADDRESS; + } + + *transport_p = _dbus_transport_debug_pipe_new (name, error); + + if (*transport_p == NULL) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT; + } + else + { + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + return DBUS_TRANSPORT_OPEN_OK; + } + } + else + { + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + return DBUS_TRANSPORT_OPEN_NOT_HANDLED; + } +} + + +/** @} */ + +#endif /* DBUS_BUILD_TESTS */ + diff --git a/src/dbus/dbus-server-debug-pipe.h b/src/dbus/dbus-server-debug-pipe.h new file mode 100644 index 0000000..e86ec5e --- /dev/null +++ b/src/dbus/dbus-server-debug-pipe.h @@ -0,0 +1,47 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-server-debug-pipe.h In-proc debug server implementation + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef DBUS_SERVER_DEBUG_PIPE_H +#define DBUS_SERVER_DEBUG_PIPE_H + +#include +#include +#include + +DBUS_BEGIN_DECLS + +DBusServer* _dbus_server_debug_pipe_new (const char *server_name, + DBusError *error); +DBusTransport* _dbus_transport_debug_pipe_new (const char *server_name, + DBusError *error); +DBusServerListenResult _dbus_server_listen_debug_pipe (DBusAddressEntry *entry, + DBusServer **server_p, + DBusError *error); +DBusTransportOpenResult _dbus_transport_open_debug_pipe (DBusAddressEntry *entry, + DBusTransport **transport_p, + DBusError *error); + + +DBUS_END_DECLS + +#endif /* DBUS_SERVER_DEBUG_PIPE_H */ diff --git a/src/dbus/dbus-server-protected.h b/src/dbus/dbus-server-protected.h new file mode 100644 index 0000000..e8ef37a --- /dev/null +++ b/src/dbus/dbus-server-protected.h @@ -0,0 +1,160 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-server-protected.h Used by subclasses of DBusServer object (internal to D-Bus implementation) + * + * Copyright (C) 2002 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef DBUS_SERVER_PROTECTED_H +#define DBUS_SERVER_PROTECTED_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DBUS_BEGIN_DECLS + +typedef struct DBusServerVTable DBusServerVTable; + +/** + * Virtual table to be implemented by all server "subclasses" + */ +struct DBusServerVTable +{ + void (* finalize) (DBusServer *server); + /**< The finalize method must free the server. */ + + void (* disconnect) (DBusServer *server); + /**< Disconnect this server. */ +}; + +/** + * Internals of DBusServer object + */ +struct DBusServer +{ + DBusAtomic refcount; /**< Reference count. */ + const DBusServerVTable *vtable; /**< Virtual methods for this instance. */ + DBusMutex *mutex; /**< Lock on the server object */ + + DBusGUID guid; /**< Globally unique ID of server */ + + DBusString guid_hex; /**< Hex-encoded version of GUID */ + + DBusWatchList *watches; /**< Our watches */ + DBusTimeoutList *timeouts; /**< Our timeouts */ + + char *address; /**< Address this server is listening on. */ + + int max_connections; /**< Max number of connections allowed at once. */ + + DBusDataSlotList slot_list; /**< Data stored by allocated integer ID */ + + DBusNewConnectionFunction new_connection_function; + /**< Callback to invoke when a new connection is created. */ + void *new_connection_data; + /**< Data for new connection callback */ + DBusFreeFunction new_connection_free_data_function; + /**< Callback to invoke to free new_connection_data + * when server is finalized or data is replaced. + */ + + char **auth_mechanisms; /**< Array of allowed authentication mechanisms */ + + unsigned int disconnected : 1; /**< TRUE if we are disconnected. */ + +#ifndef DBUS_DISABLE_CHECKS + unsigned int have_server_lock : 1; /**< Does someone have the server mutex locked */ +#endif +}; + +dbus_bool_t _dbus_server_init_base (DBusServer *server, + const DBusServerVTable *vtable, + const DBusString *address); +void _dbus_server_finalize_base (DBusServer *server); +dbus_bool_t _dbus_server_add_watch (DBusServer *server, + DBusWatch *watch); +void _dbus_server_remove_watch (DBusServer *server, + DBusWatch *watch); +void _dbus_server_toggle_watch (DBusServer *server, + DBusWatch *watch, + dbus_bool_t enabled); +dbus_bool_t _dbus_server_add_timeout (DBusServer *server, + DBusTimeout *timeout); +void _dbus_server_remove_timeout (DBusServer *server, + DBusTimeout *timeout); +void _dbus_server_toggle_timeout (DBusServer *server, + DBusTimeout *timeout, + dbus_bool_t enabled); + +void _dbus_server_ref_unlocked (DBusServer *server); +void _dbus_server_unref_unlocked (DBusServer *server); + +typedef enum +{ + DBUS_SERVER_LISTEN_NOT_HANDLED, /**< we aren't in charge of this address type */ + DBUS_SERVER_LISTEN_OK, /**< we set up the listen */ + DBUS_SERVER_LISTEN_BAD_ADDRESS, /**< malformed address */ + DBUS_SERVER_LISTEN_DID_NOT_CONNECT /**< well-formed address but failed to set it up */ +} DBusServerListenResult; + +DBusServerListenResult _dbus_server_listen_platform_specific (DBusAddressEntry *entry, + DBusServer **server_p, + DBusError *error); + +#ifdef DBUS_DISABLE_CHECKS +#define TOOK_LOCK_CHECK(server) +#define RELEASING_LOCK_CHECK(server) +#define HAVE_LOCK_CHECK(server) +#else +#define TOOK_LOCK_CHECK(server) do { \ + _dbus_assert (!(server)->have_server_lock); \ + (server)->have_server_lock = TRUE; \ + } while (0) +#define RELEASING_LOCK_CHECK(server) do { \ + _dbus_assert ((server)->have_server_lock); \ + (server)->have_server_lock = FALSE; \ + } while (0) +#define HAVE_LOCK_CHECK(server) _dbus_assert ((server)->have_server_lock) +/* A "DO_NOT_HAVE_LOCK_CHECK" is impossible since we need the lock to check the flag */ +#endif + +#define TRACE_LOCKS 0 + +#define SERVER_LOCK(server) do { \ + if (TRACE_LOCKS) { _dbus_verbose (" LOCK: %s\n", _DBUS_FUNCTION_NAME); } \ + _dbus_mutex_lock ((server)->mutex); \ + TOOK_LOCK_CHECK (server); \ + } while (0) + +#define SERVER_UNLOCK(server) do { \ + if (TRACE_LOCKS) { _dbus_verbose (" UNLOCK: %s\n", _DBUS_FUNCTION_NAME); } \ + RELEASING_LOCK_CHECK (server); \ + _dbus_mutex_unlock ((server)->mutex); \ + } while (0) + +DBUS_END_DECLS + +#endif /* DBUS_SERVER_PROTECTED_H */ diff --git a/src/dbus/dbus-server-socket.c b/src/dbus/dbus-server-socket.c new file mode 100644 index 0000000..0cd2bb6 --- /dev/null +++ b/src/dbus/dbus-server-socket.c @@ -0,0 +1,539 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-server-socket.c Server implementation for sockets + * + * Copyright (C) 2002, 2003, 2004, 2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-internals.h" +#include "dbus-server-socket.h" +#include "dbus-transport-socket.h" +#include "dbus-connection-internal.h" +#include "dbus-string.h" + +/** + * @defgroup DBusServerSocket DBusServer implementations for SOCKET + * @ingroup DBusInternals + * @brief Implementation details of DBusServer on SOCKET + * + * @{ + */ +/** + * + * Opaque object representing a Socket server implementation. + */ +typedef struct DBusServerSocket DBusServerSocket; + +/** + * Implementation details of DBusServerSocket. All members + * are private. + */ +struct DBusServerSocket +{ + DBusServer base; /**< Parent class members. */ + int n_fds; /**< Number of active file handles */ + int *fds; /**< File descriptor or -1 if disconnected. */ + DBusWatch **watch; /**< File descriptor watch. */ + char *socket_name; /**< Name of domain socket, to unlink if appropriate */ +}; + +static void +socket_finalize (DBusServer *server) +{ + DBusServerSocket *socket_server = (DBusServerSocket*) server; + int i; + + _dbus_server_finalize_base (server); + + for (i = 0 ; i < socket_server->n_fds ; i++) + if (socket_server->watch[i]) + { + _dbus_watch_unref (socket_server->watch[i]); + socket_server->watch[i] = NULL; + } + + dbus_free (socket_server->fds); + dbus_free (socket_server->watch); + dbus_free (socket_server->socket_name); + dbus_free (server); +} + +/* Return value is just for memory, not other failures. */ +static dbus_bool_t +handle_new_client_fd_and_unlock (DBusServer *server, + int client_fd) +{ + DBusConnection *connection; + DBusTransport *transport; + DBusNewConnectionFunction new_connection_function; + void *new_connection_data; + + _dbus_verbose ("Creating new client connection with fd %d\n", client_fd); + + HAVE_LOCK_CHECK (server); + + if (!_dbus_set_fd_nonblocking (client_fd, NULL)) + { + SERVER_UNLOCK (server); + return TRUE; + } + + transport = _dbus_transport_new_for_socket (client_fd, &server->guid_hex, NULL); + if (transport == NULL) + { + _dbus_close_socket (client_fd, NULL); + SERVER_UNLOCK (server); + return FALSE; + } + + if (!_dbus_transport_set_auth_mechanisms (transport, + (const char **) server->auth_mechanisms)) + { + _dbus_transport_unref (transport); + SERVER_UNLOCK (server); + return FALSE; + } + + /* note that client_fd is now owned by the transport, and will be + * closed on transport disconnection/finalization + */ + + connection = _dbus_connection_new_for_transport (transport); + _dbus_transport_unref (transport); + transport = NULL; /* now under the connection lock */ + + if (connection == NULL) + { + SERVER_UNLOCK (server); + return FALSE; + } + + /* See if someone wants to handle this new connection, self-referencing + * for paranoia. + */ + new_connection_function = server->new_connection_function; + new_connection_data = server->new_connection_data; + + _dbus_server_ref_unlocked (server); + SERVER_UNLOCK (server); + + if (new_connection_function) + { + (* new_connection_function) (server, connection, + new_connection_data); + } + dbus_server_unref (server); + + /* If no one grabbed a reference, the connection will die. */ + _dbus_connection_close_if_only_one_ref (connection); + dbus_connection_unref (connection); + + return TRUE; +} + +static dbus_bool_t +socket_handle_watch (DBusWatch *watch, + unsigned int flags, + void *data) +{ + DBusServer *server = data; +#ifndef DBUS_DISABLE_ASSERT + DBusServerSocket *socket_server = data; + int i; + dbus_bool_t found = FALSE; +#endif + + SERVER_LOCK (server); + +#ifndef DBUS_DISABLE_ASSERT + for (i = 0 ; i < socket_server->n_fds ; i++) + { + if (socket_server->watch[i] == watch) + found = TRUE; + } + _dbus_assert (found); +#endif + + _dbus_verbose ("Handling client connection, flags 0x%x\n", flags); + + if (flags & DBUS_WATCH_READABLE) + { + int client_fd; + int listen_fd; + + listen_fd = dbus_watch_get_socket (watch); + + client_fd = _dbus_accept (listen_fd); + + if (client_fd < 0) + { + /* EINTR handled for us */ + + if (_dbus_get_is_errno_eagain_or_ewouldblock ()) + _dbus_verbose ("No client available to accept after all\n"); + else + _dbus_verbose ("Failed to accept a client connection: %s\n", + _dbus_strerror_from_errno ()); + + SERVER_UNLOCK (server); + } + else + { + _dbus_fd_set_close_on_exec (client_fd); + + if (!handle_new_client_fd_and_unlock (server, client_fd)) + _dbus_verbose ("Rejected client connection due to lack of memory\n"); + } + } + + if (flags & DBUS_WATCH_ERROR) + _dbus_verbose ("Error on server listening socket\n"); + + if (flags & DBUS_WATCH_HANGUP) + _dbus_verbose ("Hangup on server listening socket\n"); + + return TRUE; +} + +static void +socket_disconnect (DBusServer *server) +{ + DBusServerSocket *socket_server = (DBusServerSocket*) server; + int i; + + HAVE_LOCK_CHECK (server); + + for (i = 0 ; i < socket_server->n_fds ; i++) + { + if (socket_server->watch[i]) + { + _dbus_server_remove_watch (server, + socket_server->watch[i]); + _dbus_watch_unref (socket_server->watch[i]); + socket_server->watch[i] = NULL; + } + + _dbus_close_socket (socket_server->fds[i], NULL); + socket_server->fds[i] = -1; + } + + if (socket_server->socket_name != NULL) + { + DBusString tmp; + _dbus_string_init_const (&tmp, socket_server->socket_name); + _dbus_delete_file (&tmp, NULL); + } + + HAVE_LOCK_CHECK (server); +} + +static const DBusServerVTable socket_vtable = { + socket_finalize, + socket_disconnect +}; + +/** + * Creates a new server listening on the given file descriptor. The + * file descriptor should be nonblocking (use + * _dbus_set_fd_nonblocking() to make it so). The file descriptor + * should be listening for connections, that is, listen() should have + * been successfully invoked on it. The server will use accept() to + * accept new client connections. + * + * @param fds list of file descriptors. + * @param n_fds number of file descriptors + * @param address the server's address + * @returns the new server, or #NULL if no memory. + * + */ +DBusServer* +_dbus_server_new_for_socket (int *fds, + int n_fds, + const DBusString *address) +{ + DBusServerSocket *socket_server; + DBusServer *server; + int i; + + socket_server = dbus_new0 (DBusServerSocket, 1); + if (socket_server == NULL) + return NULL; + + socket_server->fds = dbus_new (int, n_fds); + if (!socket_server->fds) + goto failed_0; + + socket_server->watch = dbus_new0 (DBusWatch *, n_fds); + if (!socket_server->watch) + goto failed_1; + + for (i = 0 ; i < n_fds ; i++) + { + DBusWatch *watch; + + watch = _dbus_watch_new (fds[i], + DBUS_WATCH_READABLE, + TRUE, + socket_handle_watch, socket_server, + NULL); + if (watch == NULL) + goto failed_2; + + socket_server->n_fds++; + socket_server->fds[i] = fds[i]; + socket_server->watch[i] = watch; + } + + if (!_dbus_server_init_base (&socket_server->base, + &socket_vtable, address)) + goto failed_2; + + server = (DBusServer*)socket_server; + + SERVER_LOCK (server); + + for (i = 0 ; i < n_fds ; i++) + { + if (!_dbus_server_add_watch (&socket_server->base, + socket_server->watch[i])) + { + int j; + for (j = 0 ; j < i ; j++) + _dbus_server_remove_watch (server, + socket_server->watch[j]); + + SERVER_UNLOCK (server); + _dbus_server_finalize_base (&socket_server->base); + goto failed_2; + } + } + + SERVER_UNLOCK (server); + + return (DBusServer*) socket_server; + + failed_2: + for (i = 0 ; i < n_fds ; i++) + { + if (socket_server->watch[i] != NULL) + { + _dbus_watch_unref (socket_server->watch[i]); + socket_server->watch[i] = NULL; + } + } + dbus_free (socket_server->watch); + + failed_1: + dbus_free (socket_server->fds); + + failed_0: + dbus_free (socket_server); + return NULL; +} + +/** + * Creates a new server listening on TCP. + * If host is NULL, it will default to localhost. + * If bind is NULL, it will default to the value for the host + * parameter, and if that is NULL, then localhost + * If bind is a hostname, it will be resolved and will listen + * on all returned addresses. + * If family is NULL, hostname resolution will try all address + * families, otherwise it can be ipv4 or ipv6 to restrict the + * addresses considered. + * + * @param host the hostname to report for the listen address + * @param bind the hostname to listen on + * @param port the port to listen on or 0 to let the OS choose + * @param family + * @param error location to store reason for failure. + * @returns the new server, or #NULL on failure. + */ +DBusServer* +_dbus_server_new_for_tcp_socket (const char *host, + const char *bind, + const char *port, + const char *family, + DBusError *error) +{ + DBusServer *server; + int *listen_fds = NULL; + int nlisten_fds = 0, i; + DBusString address; + DBusString host_str; + DBusString port_str; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + if (!_dbus_string_init (&address)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return NULL; + } + + if (!_dbus_string_init (&port_str)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto failed_0; + } + + if (host == NULL) + host = "localhost"; + + if (port == NULL) + port = "0"; + + if (bind == NULL) + bind = host; + else if (strcmp (bind, "*") == 0) + bind = NULL; + + nlisten_fds =_dbus_listen_tcp_socket (bind, port, family, + &port_str, + &listen_fds, error); + if (nlisten_fds <= 0) + { + _DBUS_ASSERT_ERROR_IS_SET(error); + goto failed_1; + } + + for (i = 0 ; i < nlisten_fds ; i++) + _dbus_fd_set_close_on_exec (listen_fds[i]); + + _dbus_string_init_const (&host_str, host); + if (!_dbus_string_append (&address, "tcp:host=") || + !_dbus_address_append_escaped (&address, &host_str) || + !_dbus_string_append (&address, ",port=") || + !_dbus_string_append (&address, _dbus_string_get_const_data(&port_str))) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto failed_2; + } + if (family && + (!_dbus_string_append (&address, ",family=") || + !_dbus_string_append (&address, family))) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto failed_2; + } + + server = _dbus_server_new_for_socket (listen_fds, nlisten_fds, &address); + if (server == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto failed_2; + } + + _dbus_string_free (&port_str); + _dbus_string_free (&address); + dbus_free(listen_fds); + + return server; + + failed_2: + for (i = 0 ; i < nlisten_fds ; i++) + _dbus_close_socket (listen_fds[i], NULL); + dbus_free(listen_fds); + + failed_1: + _dbus_string_free (&port_str); + + failed_0: + _dbus_string_free (&address); + + return NULL; +} + +/** + * Tries to interpret the address entry for various socket-related + * addresses (well, currently only tcp). + * + * Sets error if the result is not OK. + * + * @param entry an address entry + * @param server_p a new DBusServer, or #NULL on failure. + * @param error location to store rationale for failure on bad address + * @returns the outcome + * + */ +DBusServerListenResult +_dbus_server_listen_socket (DBusAddressEntry *entry, + DBusServer **server_p, + DBusError *error) +{ + const char *method; + + *server_p = NULL; + + method = dbus_address_entry_get_method (entry); + + if (strcmp (method, "tcp") == 0) + { + const char *host; + const char *port; + const char *bind; + const char *family; + + host = dbus_address_entry_get_value (entry, "host"); + bind = dbus_address_entry_get_value (entry, "bind"); + port = dbus_address_entry_get_value (entry, "port"); + family = dbus_address_entry_get_value (entry, "family"); + + *server_p = _dbus_server_new_for_tcp_socket (host, bind, port, + family, error); + + if (*server_p) + { + _DBUS_ASSERT_ERROR_IS_CLEAR(error); + return DBUS_SERVER_LISTEN_OK; + } + else + { + _DBUS_ASSERT_ERROR_IS_SET(error); + return DBUS_SERVER_LISTEN_DID_NOT_CONNECT; + } + } + else + { + _DBUS_ASSERT_ERROR_IS_CLEAR(error); + return DBUS_SERVER_LISTEN_NOT_HANDLED; + } +} + +/** + * This is a bad hack since it's really unix domain socket + * specific. Also, the function weirdly adopts ownership + * of the passed-in string. + * + * @param server a socket server + * @param filename socket filename to report/delete + * + */ +void +_dbus_server_socket_own_filename (DBusServer *server, + char *filename) +{ + DBusServerSocket *socket_server = (DBusServerSocket*) server; + + socket_server->socket_name = filename; +} + + +/** @} */ + diff --git a/src/dbus/dbus-server-socket.h b/src/dbus/dbus-server-socket.h new file mode 100644 index 0000000..34900b4 --- /dev/null +++ b/src/dbus/dbus-server-socket.h @@ -0,0 +1,49 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-server-socket.h Server implementation for sockets + * + * Copyright (C) 2002, 2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef DBUS_SERVER_SOCKET_H +#define DBUS_SERVER_SOCKET_H + +#include +#include + +DBUS_BEGIN_DECLS + +DBusServer* _dbus_server_new_for_socket (int *fds, + int n_fds, + const DBusString *address); +DBusServer* _dbus_server_new_for_tcp_socket (const char *host, + const char *bind, + const char *port, + const char *family, + DBusError *error); +DBusServerListenResult _dbus_server_listen_socket (DBusAddressEntry *entry, + DBusServer **server_p, + DBusError *error); + + +void _dbus_server_socket_own_filename (DBusServer *server, + char *filename); + +DBUS_END_DECLS + +#endif /* DBUS_SERVER_SOCKET_H */ diff --git a/src/dbus/dbus-server-unix.c b/src/dbus/dbus-server-unix.c new file mode 100644 index 0000000..1dda5d1 --- /dev/null +++ b/src/dbus/dbus-server-unix.c @@ -0,0 +1,236 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-server-unix.c Server implementation for Unix network protocols. + * + * Copyright (C) 2002, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-internals.h" +#include "dbus-server-unix.h" +#include "dbus-server-socket.h" +#include "dbus-transport-unix.h" +#include "dbus-connection-internal.h" +#include "dbus-sysdeps-unix.h" +#include "dbus-string.h" + +/** + * @defgroup DBusServerUnix DBusServer implementations for UNIX + * @ingroup DBusInternals + * @brief Implementation details of DBusServer on UNIX + * + * @{ + */ + +/** + * Tries to interpret the address entry in a platform-specific + * way, creating a platform-specific server type if appropriate. + * Sets error if the result is not OK. + * + * @param entry an address entry + * @param server_p location to store a new DBusServer, or #NULL on failure. + * @param error location to store rationale for failure on bad address + * @returns the outcome + * + */ +DBusServerListenResult +_dbus_server_listen_platform_specific (DBusAddressEntry *entry, + DBusServer **server_p, + DBusError *error) +{ + const char *method; + + *server_p = NULL; + + method = dbus_address_entry_get_method (entry); + + if (strcmp (method, "unix") == 0) + { + const char *path = dbus_address_entry_get_value (entry, "path"); + const char *tmpdir = dbus_address_entry_get_value (entry, "tmpdir"); + const char *abstract = dbus_address_entry_get_value (entry, "abstract"); + + if (path == NULL && tmpdir == NULL && abstract == NULL) + { + _dbus_set_bad_address(error, "unix", + "path or tmpdir or abstract", + NULL); + return DBUS_SERVER_LISTEN_BAD_ADDRESS; + } + + if ((path && tmpdir) || + (path && abstract) || + (tmpdir && abstract)) + { + _dbus_set_bad_address(error, NULL, NULL, + "cannot specify two of \"path\" and \"tmpdir\" and \"abstract\" at the same time"); + return DBUS_SERVER_LISTEN_BAD_ADDRESS; + } + + if (tmpdir != NULL) + { + DBusString full_path; + DBusString filename; + + if (!_dbus_string_init (&full_path)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return DBUS_SERVER_LISTEN_DID_NOT_CONNECT; + } + + if (!_dbus_string_init (&filename)) + { + _dbus_string_free (&full_path); + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return DBUS_SERVER_LISTEN_DID_NOT_CONNECT; + } + + if (!_dbus_string_append (&filename, + "dbus-") || + !_dbus_generate_random_ascii (&filename, 10) || + !_dbus_string_append (&full_path, tmpdir) || + !_dbus_concat_dir_and_file (&full_path, &filename)) + { + _dbus_string_free (&full_path); + _dbus_string_free (&filename); + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return DBUS_SERVER_LISTEN_DID_NOT_CONNECT; + } + + /* Always use abstract namespace if possible with tmpdir */ + + *server_p = + _dbus_server_new_for_domain_socket (_dbus_string_get_const_data (&full_path), +#ifdef HAVE_ABSTRACT_SOCKETS + TRUE, +#else + FALSE, +#endif + error); + + _dbus_string_free (&full_path); + _dbus_string_free (&filename); + } + else + { + if (path) + *server_p = _dbus_server_new_for_domain_socket (path, FALSE, error); + else + *server_p = _dbus_server_new_for_domain_socket (abstract, TRUE, error); + } + + if (*server_p != NULL) + { + _DBUS_ASSERT_ERROR_IS_CLEAR(error); + return DBUS_SERVER_LISTEN_OK; + } + else + { + _DBUS_ASSERT_ERROR_IS_SET(error); + return DBUS_SERVER_LISTEN_DID_NOT_CONNECT; + } + } + else + { + /* If we don't handle the method, we return NULL with the + * error unset + */ + _DBUS_ASSERT_ERROR_IS_CLEAR(error); + return DBUS_SERVER_LISTEN_NOT_HANDLED; + } +} + +/** + * Creates a new server listening on the given Unix domain socket. + * + * @param path the path for the domain socket. + * @param abstract #TRUE to use abstract socket namespace + * @param error location to store reason for failure. + * @returns the new server, or #NULL on failure. + */ +DBusServer* +_dbus_server_new_for_domain_socket (const char *path, + dbus_bool_t abstract, + DBusError *error) +{ + DBusServer *server; + int listen_fd; + DBusString address; + char *path_copy; + DBusString path_str; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + if (!_dbus_string_init (&address)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return NULL; + } + + _dbus_string_init_const (&path_str, path); + if ((abstract && + !_dbus_string_append (&address, "unix:abstract=")) || + (!abstract && + !_dbus_string_append (&address, "unix:path=")) || + !_dbus_address_append_escaped (&address, &path_str)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto failed_0; + } + + path_copy = _dbus_strdup (path); + if (path_copy == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto failed_0; + } + + listen_fd = _dbus_listen_unix_socket (path, abstract, error); + _dbus_fd_set_close_on_exec (listen_fd); + + if (listen_fd < 0) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + goto failed_1; + } + + server = _dbus_server_new_for_socket (&listen_fd, 1, &address); + if (server == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto failed_2; + } + + _dbus_server_socket_own_filename(server, path_copy); + + _dbus_string_free (&address); + + return server; + + failed_2: + _dbus_close_socket (listen_fd, NULL); + failed_1: + dbus_free (path_copy); + failed_0: + _dbus_string_free (&address); + + return NULL; +} + +/** @} */ + diff --git a/src/dbus/dbus-server-unix.h b/src/dbus/dbus-server-unix.h new file mode 100644 index 0000000..34f5a71 --- /dev/null +++ b/src/dbus/dbus-server-unix.h @@ -0,0 +1,37 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-server-unix.h Server implementation for Unix network protocols. + * + * Copyright (C) 2002 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef DBUS_SERVER_UNIX_H +#define DBUS_SERVER_UNIX_H + +#include +#include + +DBUS_BEGIN_DECLS + +DBusServer* _dbus_server_new_for_domain_socket (const char *path, + dbus_bool_t abstract, + DBusError *error); + +DBUS_END_DECLS + +#endif /* DBUS_SERVER_UNIX_H */ diff --git a/src/dbus/dbus-server.c b/src/dbus/dbus-server.c new file mode 100644 index 0000000..f04829b --- /dev/null +++ b/src/dbus/dbus-server.c @@ -0,0 +1,1202 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-server.c DBusServer object + * + * Copyright (C) 2002, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-server.h" +#include "dbus-server-unix.h" +#include "dbus-server-socket.h" +#include "dbus-string.h" +#ifdef DBUS_BUILD_TESTS +#include "dbus-server-debug-pipe.h" +#endif +#include "dbus-address.h" +#include "dbus-protocol.h" + +/** + * @defgroup DBusServer DBusServer + * @ingroup DBus + * @brief Server that listens for new connections. + * + * A DBusServer represents a server that other applications + * can connect to. Each connection from another application + * is represented by a #DBusConnection. + * + * @todo Thread safety hasn't been tested much for #DBusServer + * @todo Need notification to apps of disconnection, may matter for some transports + */ + +/** + * @defgroup DBusServerInternals DBusServer implementation details + * @ingroup DBusInternals + * @brief Implementation details of DBusServer + * + * @{ + */ + +/* this is a little fragile since it assumes the address doesn't + * already have a guid, but it shouldn't + */ +static char* +copy_address_with_guid_appended (const DBusString *address, + const DBusString *guid_hex) +{ + DBusString with_guid; + char *retval; + + if (!_dbus_string_init (&with_guid)) + return NULL; + + if (!_dbus_string_copy (address, 0, &with_guid, + _dbus_string_get_length (&with_guid)) || + !_dbus_string_append (&with_guid, ",guid=") || + !_dbus_string_copy (guid_hex, 0, + &with_guid, _dbus_string_get_length (&with_guid))) + { + _dbus_string_free (&with_guid); + return NULL; + } + + retval = NULL; + _dbus_string_steal_data (&with_guid, &retval); + + _dbus_string_free (&with_guid); + + return retval; /* may be NULL if steal_data failed */ +} + +/** + * Initializes the members of the DBusServer base class. + * Chained up to by subclass constructors. + * + * @param server the server. + * @param vtable the vtable for the subclass. + * @param address the server's address + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_server_init_base (DBusServer *server, + const DBusServerVTable *vtable, + const DBusString *address) +{ + server->vtable = vtable; + server->refcount.value = 1; + + server->address = NULL; + server->watches = NULL; + server->timeouts = NULL; + + if (!_dbus_string_init (&server->guid_hex)) + return FALSE; + + _dbus_generate_uuid (&server->guid); + + if (!_dbus_uuid_encode (&server->guid, &server->guid_hex)) + goto failed; + + server->address = copy_address_with_guid_appended (address, + &server->guid_hex); + if (server->address == NULL) + goto failed; + + _dbus_mutex_new_at_location (&server->mutex); + if (server->mutex == NULL) + goto failed; + + server->watches = _dbus_watch_list_new (); + if (server->watches == NULL) + goto failed; + + server->timeouts = _dbus_timeout_list_new (); + if (server->timeouts == NULL) + goto failed; + + _dbus_data_slot_list_init (&server->slot_list); + + _dbus_verbose ("Initialized server on address %s\n", server->address); + + return TRUE; + + failed: + _dbus_mutex_free_at_location (&server->mutex); + server->mutex = NULL; + if (server->watches) + { + _dbus_watch_list_free (server->watches); + server->watches = NULL; + } + if (server->timeouts) + { + _dbus_timeout_list_free (server->timeouts); + server->timeouts = NULL; + } + if (server->address) + { + dbus_free (server->address); + server->address = NULL; + } + _dbus_string_free (&server->guid_hex); + + return FALSE; +} + +/** + * Finalizes the members of the DBusServer base class. + * Chained up to by subclass finalizers. + * + * @param server the server. + */ +void +_dbus_server_finalize_base (DBusServer *server) +{ + /* We don't have the lock, but nobody should be accessing + * concurrently since they don't have a ref + */ +#ifndef DBUS_DISABLE_CHECKS + _dbus_assert (!server->have_server_lock); +#endif + _dbus_assert (server->disconnected); + + /* calls out to application code... */ + _dbus_data_slot_list_free (&server->slot_list); + + dbus_server_set_new_connection_function (server, NULL, NULL, NULL); + + _dbus_watch_list_free (server->watches); + _dbus_timeout_list_free (server->timeouts); + + _dbus_mutex_free_at_location (&server->mutex); + + dbus_free (server->address); + + dbus_free_string_array (server->auth_mechanisms); + + _dbus_string_free (&server->guid_hex); +} + + +/** Function to be called in protected_change_watch() with refcount held */ +typedef dbus_bool_t (* DBusWatchAddFunction) (DBusWatchList *list, + DBusWatch *watch); +/** Function to be called in protected_change_watch() with refcount held */ +typedef void (* DBusWatchRemoveFunction) (DBusWatchList *list, + DBusWatch *watch); +/** Function to be called in protected_change_watch() with refcount held */ +typedef void (* DBusWatchToggleFunction) (DBusWatchList *list, + DBusWatch *watch, + dbus_bool_t enabled); + +static dbus_bool_t +protected_change_watch (DBusServer *server, + DBusWatch *watch, + DBusWatchAddFunction add_function, + DBusWatchRemoveFunction remove_function, + DBusWatchToggleFunction toggle_function, + dbus_bool_t enabled) +{ + DBusWatchList *watches; + dbus_bool_t retval; + + HAVE_LOCK_CHECK (server); + + /* This isn't really safe or reasonable; a better pattern is the "do + * everything, then drop lock and call out" one; but it has to be + * propagated up through all callers + */ + + watches = server->watches; + if (watches) + { + server->watches = NULL; + _dbus_server_ref_unlocked (server); + SERVER_UNLOCK (server); + + if (add_function) + retval = (* add_function) (watches, watch); + else if (remove_function) + { + retval = TRUE; + (* remove_function) (watches, watch); + } + else + { + retval = TRUE; + (* toggle_function) (watches, watch, enabled); + } + + SERVER_LOCK (server); + server->watches = watches; + _dbus_server_unref_unlocked (server); + + return retval; + } + else + return FALSE; +} + +/** + * Adds a watch for this server, chaining out to application-provided + * watch handlers. + * + * @param server the server. + * @param watch the watch to add. + */ +dbus_bool_t +_dbus_server_add_watch (DBusServer *server, + DBusWatch *watch) +{ + HAVE_LOCK_CHECK (server); + return protected_change_watch (server, watch, + _dbus_watch_list_add_watch, + NULL, NULL, FALSE); +} + +/** + * Removes a watch previously added with _dbus_server_remove_watch(). + * + * @param server the server. + * @param watch the watch to remove. + */ +void +_dbus_server_remove_watch (DBusServer *server, + DBusWatch *watch) +{ + HAVE_LOCK_CHECK (server); + protected_change_watch (server, watch, + NULL, + _dbus_watch_list_remove_watch, + NULL, FALSE); +} + +/** + * Toggles a watch and notifies app via server's + * DBusWatchToggledFunction if available. It's an error to call this + * function on a watch that was not previously added. + * + * @param server the server. + * @param watch the watch to toggle. + * @param enabled whether to enable or disable + */ +void +_dbus_server_toggle_watch (DBusServer *server, + DBusWatch *watch, + dbus_bool_t enabled) +{ + _dbus_assert (watch != NULL); + + HAVE_LOCK_CHECK (server); + protected_change_watch (server, watch, + NULL, NULL, + _dbus_watch_list_toggle_watch, + enabled); +} + +/** Function to be called in protected_change_timeout() with refcount held */ +typedef dbus_bool_t (* DBusTimeoutAddFunction) (DBusTimeoutList *list, + DBusTimeout *timeout); +/** Function to be called in protected_change_timeout() with refcount held */ +typedef void (* DBusTimeoutRemoveFunction) (DBusTimeoutList *list, + DBusTimeout *timeout); +/** Function to be called in protected_change_timeout() with refcount held */ +typedef void (* DBusTimeoutToggleFunction) (DBusTimeoutList *list, + DBusTimeout *timeout, + dbus_bool_t enabled); + + +static dbus_bool_t +protected_change_timeout (DBusServer *server, + DBusTimeout *timeout, + DBusTimeoutAddFunction add_function, + DBusTimeoutRemoveFunction remove_function, + DBusTimeoutToggleFunction toggle_function, + dbus_bool_t enabled) +{ + DBusTimeoutList *timeouts; + dbus_bool_t retval; + + HAVE_LOCK_CHECK (server); + + /* This isn't really safe or reasonable; a better pattern is the "do everything, then + * drop lock and call out" one; but it has to be propagated up through all callers + */ + + timeouts = server->timeouts; + if (timeouts) + { + server->timeouts = NULL; + _dbus_server_ref_unlocked (server); + SERVER_UNLOCK (server); + + if (add_function) + retval = (* add_function) (timeouts, timeout); + else if (remove_function) + { + retval = TRUE; + (* remove_function) (timeouts, timeout); + } + else + { + retval = TRUE; + (* toggle_function) (timeouts, timeout, enabled); + } + + SERVER_LOCK (server); + server->timeouts = timeouts; + _dbus_server_unref_unlocked (server); + + return retval; + } + else + return FALSE; +} + +/** + * Adds a timeout for this server, chaining out to + * application-provided timeout handlers. The timeout should be + * repeatedly handled with dbus_timeout_handle() at its given interval + * until it is removed. + * + * @param server the server. + * @param timeout the timeout to add. + */ +dbus_bool_t +_dbus_server_add_timeout (DBusServer *server, + DBusTimeout *timeout) +{ + return protected_change_timeout (server, timeout, + _dbus_timeout_list_add_timeout, + NULL, NULL, FALSE); +} + +/** + * Removes a timeout previously added with _dbus_server_add_timeout(). + * + * @param server the server. + * @param timeout the timeout to remove. + */ +void +_dbus_server_remove_timeout (DBusServer *server, + DBusTimeout *timeout) +{ + protected_change_timeout (server, timeout, + NULL, + _dbus_timeout_list_remove_timeout, + NULL, FALSE); +} + +/** + * Toggles a timeout and notifies app via server's + * DBusTimeoutToggledFunction if available. It's an error to call this + * function on a timeout that was not previously added. + * + * @param server the server. + * @param timeout the timeout to toggle. + * @param enabled whether to enable or disable + */ +void +_dbus_server_toggle_timeout (DBusServer *server, + DBusTimeout *timeout, + dbus_bool_t enabled) +{ + protected_change_timeout (server, timeout, + NULL, NULL, + _dbus_timeout_list_toggle_timeout, + enabled); +} + + +/** + * Like dbus_server_ref() but does not acquire the lock (must already be held) + * + * @param server the server. + */ +void +_dbus_server_ref_unlocked (DBusServer *server) +{ + _dbus_assert (server != NULL); + _dbus_assert (server->refcount.value > 0); + + HAVE_LOCK_CHECK (server); + +#ifdef DBUS_HAVE_ATOMIC_INT + _dbus_atomic_inc (&server->refcount); +#else + _dbus_assert (server->refcount.value > 0); + + server->refcount.value += 1; +#endif +} + +/** + * Like dbus_server_unref() but does not acquire the lock (must already be held) + * + * @param server the server. + */ +void +_dbus_server_unref_unlocked (DBusServer *server) +{ + dbus_bool_t last_unref; + + /* Keep this in sync with dbus_server_unref */ + + _dbus_assert (server != NULL); + _dbus_assert (server->refcount.value > 0); + + HAVE_LOCK_CHECK (server); + +#ifdef DBUS_HAVE_ATOMIC_INT + last_unref = (_dbus_atomic_dec (&server->refcount) == 1); +#else + _dbus_assert (server->refcount.value > 0); + + server->refcount.value -= 1; + last_unref = (server->refcount.value == 0); +#endif + + if (last_unref) + { + _dbus_assert (server->disconnected); + + SERVER_UNLOCK (server); + + _dbus_assert (server->vtable->finalize != NULL); + + (* server->vtable->finalize) (server); + } +} + +/** @} */ + +/** + * @addtogroup DBusServer + * + * @{ + */ + + +/** + * @typedef DBusServer + * + * An opaque object representing a server that listens for + * connections from other applications. Each time a connection + * is made, a new DBusConnection is created and made available + * via an application-provided DBusNewConnectionFunction. + * The DBusNewConnectionFunction is provided with + * dbus_server_set_new_connection_function(). + * + */ + +static const struct { + DBusServerListenResult (* func) (DBusAddressEntry *entry, + DBusServer **server_p, + DBusError *error); +} listen_funcs[] = { + { _dbus_server_listen_socket } + , { _dbus_server_listen_platform_specific } +#ifdef DBUS_BUILD_TESTS + , { _dbus_server_listen_debug_pipe } +#endif +}; + +/** + * Listens for new connections on the given address. If there are + * multiple semicolon-separated address entries in the address, tries + * each one and listens on the first one that works. + * + * Returns #NULL and sets error if listening fails for any reason. + * Otherwise returns a new #DBusServer. + * dbus_server_set_new_connection_function(), + * dbus_server_set_watch_functions(), and + * dbus_server_set_timeout_functions() should be called immediately to + * render the server fully functional. + * + * To free the server, applications must call first + * dbus_server_disconnect() and then dbus_server_unref(). + * + * @param address the address of this server. + * @param error location to store reason for failure. + * @returns a new #DBusServer, or #NULL on failure. + * + */ +DBusServer* +dbus_server_listen (const char *address, + DBusError *error) +{ + DBusServer *server; + DBusAddressEntry **entries; + int len, i; + DBusError first_connect_error = DBUS_ERROR_INIT; + dbus_bool_t handled_once; + + _dbus_return_val_if_fail (address != NULL, NULL); + _dbus_return_val_if_error_is_set (error, NULL); + + if (!dbus_parse_address (address, &entries, &len, error)) + return NULL; + + server = NULL; + handled_once = FALSE; + + for (i = 0; i < len; i++) + { + int j; + + for (j = 0; j < (int) _DBUS_N_ELEMENTS (listen_funcs); ++j) + { + DBusServerListenResult result; + DBusError tmp_error = DBUS_ERROR_INIT; + + result = (* listen_funcs[j].func) (entries[i], + &server, + &tmp_error); + + if (result == DBUS_SERVER_LISTEN_OK) + { + _dbus_assert (server != NULL); + _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error); + handled_once = TRUE; + goto out; + } + else if (result == DBUS_SERVER_LISTEN_BAD_ADDRESS) + { + _dbus_assert (server == NULL); + _DBUS_ASSERT_ERROR_IS_SET (&tmp_error); + dbus_move_error (&tmp_error, error); + handled_once = TRUE; + goto out; + } + else if (result == DBUS_SERVER_LISTEN_NOT_HANDLED) + { + _dbus_assert (server == NULL); + _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error); + + /* keep trying addresses */ + } + else if (result == DBUS_SERVER_LISTEN_DID_NOT_CONNECT) + { + _dbus_assert (server == NULL); + _DBUS_ASSERT_ERROR_IS_SET (&tmp_error); + if (!dbus_error_is_set (&first_connect_error)) + dbus_move_error (&tmp_error, &first_connect_error); + else + dbus_error_free (&tmp_error); + + handled_once = TRUE; + + /* keep trying addresses */ + } + } + + _dbus_assert (server == NULL); + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + } + + out: + + if (!handled_once) + { + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + if (len > 0) + dbus_set_error (error, + DBUS_ERROR_BAD_ADDRESS, + "Unknown address type '%s'", + dbus_address_entry_get_method (entries[0])); + else + dbus_set_error (error, + DBUS_ERROR_BAD_ADDRESS, + "Empty address '%s'", + address); + } + + dbus_address_entries_free (entries); + + if (server == NULL) + { + _dbus_assert (error == NULL || dbus_error_is_set (&first_connect_error) || + dbus_error_is_set (error)); + + if (error && dbus_error_is_set (error)) + { + /* already set the error */ + } + else + { + /* didn't set the error but either error should be + * NULL or first_connect_error should be set. + */ + _dbus_assert (error == NULL || dbus_error_is_set (&first_connect_error)); + dbus_move_error (&first_connect_error, error); + } + + _DBUS_ASSERT_ERROR_IS_CLEAR (&first_connect_error); /* be sure we freed it */ + _DBUS_ASSERT_ERROR_IS_SET (error); + + return NULL; + } + else + { + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + return server; + } +} + +/** + * Increments the reference count of a DBusServer. + * + * @param server the server. + * @returns the server + */ +DBusServer * +dbus_server_ref (DBusServer *server) +{ + _dbus_return_val_if_fail (server != NULL, NULL); + _dbus_return_val_if_fail (server->refcount.value > 0, NULL); + +#ifdef DBUS_HAVE_ATOMIC_INT + _dbus_atomic_inc (&server->refcount); +#else + SERVER_LOCK (server); + _dbus_assert (server->refcount.value > 0); + + server->refcount.value += 1; + SERVER_UNLOCK (server); +#endif + + return server; +} + +/** + * Decrements the reference count of a DBusServer. Finalizes the + * server if the reference count reaches zero. + * + * The server must be disconnected before the refcount reaches zero. + * + * @param server the server. + */ +void +dbus_server_unref (DBusServer *server) +{ + dbus_bool_t last_unref; + + /* keep this in sync with unref_unlocked */ + + _dbus_return_if_fail (server != NULL); + _dbus_return_if_fail (server->refcount.value > 0); + +#ifdef DBUS_HAVE_ATOMIC_INT + last_unref = (_dbus_atomic_dec (&server->refcount) == 1); +#else + SERVER_LOCK (server); + + _dbus_assert (server->refcount.value > 0); + + server->refcount.value -= 1; + last_unref = (server->refcount.value == 0); + + SERVER_UNLOCK (server); +#endif + + if (last_unref) + { + /* lock not held! */ + _dbus_assert (server->disconnected); + + _dbus_assert (server->vtable->finalize != NULL); + + (* server->vtable->finalize) (server); + } +} + +/** + * Releases the server's address and stops listening for + * new clients. If called more than once, only the first + * call has an effect. Does not modify the server's + * reference count. + * + * @param server the server. + */ +void +dbus_server_disconnect (DBusServer *server) +{ + _dbus_return_if_fail (server != NULL); + _dbus_return_if_fail (server->refcount.value > 0); + + SERVER_LOCK (server); + _dbus_server_ref_unlocked (server); + + _dbus_assert (server->vtable->disconnect != NULL); + + if (!server->disconnected) + { + /* this has to be first so recursive calls to disconnect don't happen */ + server->disconnected = TRUE; + + (* server->vtable->disconnect) (server); + } + + SERVER_UNLOCK (server); + dbus_server_unref (server); +} + +/** + * Returns #TRUE if the server is still listening for new connections. + * + * @param server the server. + */ +dbus_bool_t +dbus_server_get_is_connected (DBusServer *server) +{ + dbus_bool_t retval; + + _dbus_return_val_if_fail (server != NULL, FALSE); + + SERVER_LOCK (server); + retval = !server->disconnected; + SERVER_UNLOCK (server); + + return retval; +} + +/** + * Returns the address of the server, as a newly-allocated + * string which must be freed by the caller. + * + * @param server the server + * @returns the address or #NULL if no memory + */ +char* +dbus_server_get_address (DBusServer *server) +{ + char *retval; + + _dbus_return_val_if_fail (server != NULL, NULL); + + SERVER_LOCK (server); + retval = _dbus_strdup (server->address); + SERVER_UNLOCK (server); + + return retval; +} + +/** + * Returns the unique ID of the server, as a newly-allocated + * string which must be freed by the caller. This ID is + * normally used by clients to tell when two #DBusConnection + * would be equivalent (because the server address passed + * to dbus_connection_open() will have the same guid in the + * two cases). dbus_connection_open() can re-use an existing + * connection with the same ID instead of opening a new + * connection. + * + * This is an ID unique to each #DBusServer. Remember that + * a #DBusServer represents only one mode of connecting, + * so e.g. a bus daemon can listen on multiple addresses + * which will mean it has multiple #DBusServer each with + * their own ID. + * + * The ID is not a UUID in the sense of RFC4122; the details + * are explained in the D-Bus specification. + * + * @param server the server + * @returns the id of the server or #NULL if no memory + */ +char* +dbus_server_get_id (DBusServer *server) +{ + char *retval; + + _dbus_return_val_if_fail (server != NULL, NULL); + + SERVER_LOCK (server); + retval = NULL; + _dbus_string_copy_data (&server->guid_hex, &retval); + SERVER_UNLOCK (server); + + return retval; +} + +/** + * Sets a function to be used for handling new connections. The given + * function is passed each new connection as the connection is + * created. If the new connection function increments the connection's + * reference count, the connection will stay alive. Otherwise, the + * connection will be unreferenced and closed. The new connection + * function may also close the connection itself, which is considered + * good form if the connection is not wanted. + * + * The connection here is private in the sense of + * dbus_connection_open_private(), so if the new connection function + * keeps a reference it must arrange for the connection to be closed. + * i.e. libdbus does not own this connection once the new connection + * function takes a reference. + * + * @param server the server. + * @param function a function to handle new connections. + * @param data data to pass to the new connection handler. + * @param free_data_function function to free the data. + */ +void +dbus_server_set_new_connection_function (DBusServer *server, + DBusNewConnectionFunction function, + void *data, + DBusFreeFunction free_data_function) +{ + DBusFreeFunction old_free_function; + void *old_data; + + _dbus_return_if_fail (server != NULL); + + SERVER_LOCK (server); + old_free_function = server->new_connection_free_data_function; + old_data = server->new_connection_data; + + server->new_connection_function = function; + server->new_connection_data = data; + server->new_connection_free_data_function = free_data_function; + SERVER_UNLOCK (server); + + if (old_free_function != NULL) + (* old_free_function) (old_data); +} + +/** + * Sets the watch functions for the server. These functions are + * responsible for making the application's main loop aware of file + * descriptors that need to be monitored for events. + * + * This function behaves exactly like dbus_connection_set_watch_functions(); + * see the documentation for that routine. + * + * @param server the server. + * @param add_function function to begin monitoring a new descriptor. + * @param remove_function function to stop monitoring a descriptor. + * @param toggled_function function to notify when the watch is enabled/disabled + * @param data data to pass to add_function and remove_function. + * @param free_data_function function to be called to free the data. + * @returns #FALSE on failure (no memory) + */ +dbus_bool_t +dbus_server_set_watch_functions (DBusServer *server, + DBusAddWatchFunction add_function, + DBusRemoveWatchFunction remove_function, + DBusWatchToggledFunction toggled_function, + void *data, + DBusFreeFunction free_data_function) +{ + dbus_bool_t result; + DBusWatchList *watches; + + _dbus_return_val_if_fail (server != NULL, FALSE); + + SERVER_LOCK (server); + watches = server->watches; + server->watches = NULL; + if (watches) + { + SERVER_UNLOCK (server); + result = _dbus_watch_list_set_functions (watches, + add_function, + remove_function, + toggled_function, + data, + free_data_function); + SERVER_LOCK (server); + } + else + { + _dbus_warn_check_failed ("Re-entrant call to %s\n", _DBUS_FUNCTION_NAME); + result = FALSE; + } + server->watches = watches; + SERVER_UNLOCK (server); + + return result; +} + +/** + * Sets the timeout functions for the server. These functions are + * responsible for making the application's main loop aware of timeouts. + * + * This function behaves exactly like dbus_connection_set_timeout_functions(); + * see the documentation for that routine. + * + * @param server the server. + * @param add_function function to add a timeout. + * @param remove_function function to remove a timeout. + * @param toggled_function function to notify when the timeout is enabled/disabled + * @param data data to pass to add_function and remove_function. + * @param free_data_function function to be called to free the data. + * @returns #FALSE on failure (no memory) + */ +dbus_bool_t +dbus_server_set_timeout_functions (DBusServer *server, + DBusAddTimeoutFunction add_function, + DBusRemoveTimeoutFunction remove_function, + DBusTimeoutToggledFunction toggled_function, + void *data, + DBusFreeFunction free_data_function) +{ + dbus_bool_t result; + DBusTimeoutList *timeouts; + + _dbus_return_val_if_fail (server != NULL, FALSE); + + SERVER_LOCK (server); + timeouts = server->timeouts; + server->timeouts = NULL; + if (timeouts) + { + SERVER_UNLOCK (server); + result = _dbus_timeout_list_set_functions (timeouts, + add_function, + remove_function, + toggled_function, + data, + free_data_function); + SERVER_LOCK (server); + } + else + { + _dbus_warn_check_failed ("Re-entrant call to %s\n", _DBUS_FUNCTION_NAME); + result = FALSE; + } + server->timeouts = timeouts; + SERVER_UNLOCK (server); + + return result; +} + +/** + * Sets the authentication mechanisms that this server offers to + * clients, as a #NULL-terminated array of mechanism names. This + * function only affects connections created after it is + * called. Pass #NULL instead of an array to use all available + * mechanisms (this is the default behavior). + * + * The D-Bus specification describes some of the supported mechanisms. + * + * @param server the server + * @param mechanisms #NULL-terminated array of mechanisms + * @returns #FALSE if no memory + */ +dbus_bool_t +dbus_server_set_auth_mechanisms (DBusServer *server, + const char **mechanisms) +{ + char **copy; + + _dbus_return_val_if_fail (server != NULL, FALSE); + + SERVER_LOCK (server); + + if (mechanisms != NULL) + { + copy = _dbus_dup_string_array (mechanisms); + if (copy == NULL) + return FALSE; + } + else + copy = NULL; + + dbus_free_string_array (server->auth_mechanisms); + server->auth_mechanisms = copy; + + SERVER_UNLOCK (server); + + return TRUE; +} + + +static DBusDataSlotAllocator slot_allocator; +_DBUS_DEFINE_GLOBAL_LOCK (server_slots); + +/** + * Allocates an integer ID to be used for storing application-specific + * data on any DBusServer. The allocated ID may then be used + * with dbus_server_set_data() and dbus_server_get_data(). + * The slot must be initialized with -1. If a nonnegative + * slot is passed in, the refcount is incremented on that + * slot, rather than creating a new slot. + * + * The allocated slot is global, i.e. all DBusServer objects will have + * a slot with the given integer ID reserved. + * + * @param slot_p address of global variable storing the slot ID + * @returns #FALSE on no memory + */ +dbus_bool_t +dbus_server_allocate_data_slot (dbus_int32_t *slot_p) +{ + return _dbus_data_slot_allocator_alloc (&slot_allocator, + (DBusMutex **)&_DBUS_LOCK_NAME (server_slots), + slot_p); +} + +/** + * Deallocates a global ID for server data slots. + * dbus_server_get_data() and dbus_server_set_data() + * may no longer be used with this slot. + * Existing data stored on existing DBusServer objects + * will be freed when the server is finalized, + * but may not be retrieved (and may only be replaced + * if someone else reallocates the slot). + * + * @param slot_p address of the slot to deallocate + */ +void +dbus_server_free_data_slot (dbus_int32_t *slot_p) +{ + _dbus_return_if_fail (*slot_p >= 0); + + _dbus_data_slot_allocator_free (&slot_allocator, slot_p); +} + +/** + * Stores a pointer on a DBusServer, along + * with an optional function to be used for freeing + * the data when the data is set again, or when + * the server is finalized. The slot number + * must have been allocated with dbus_server_allocate_data_slot(). + * + * @param server the server + * @param slot the slot number + * @param data the data to store + * @param free_data_func finalizer function for the data + * @returns #TRUE if there was enough memory to store the data + */ +dbus_bool_t +dbus_server_set_data (DBusServer *server, + int slot, + void *data, + DBusFreeFunction free_data_func) +{ + DBusFreeFunction old_free_func; + void *old_data; + dbus_bool_t retval; + + _dbus_return_val_if_fail (server != NULL, FALSE); + + SERVER_LOCK (server); + + retval = _dbus_data_slot_list_set (&slot_allocator, + &server->slot_list, + slot, data, free_data_func, + &old_free_func, &old_data); + + + SERVER_UNLOCK (server); + + if (retval) + { + /* Do the actual free outside the server lock */ + if (old_free_func) + (* old_free_func) (old_data); + } + + return retval; +} + +/** + * Retrieves data previously set with dbus_server_set_data(). + * The slot must still be allocated (must not have been freed). + * + * @param server the server + * @param slot the slot to get data from + * @returns the data, or #NULL if not found + */ +void* +dbus_server_get_data (DBusServer *server, + int slot) +{ + void *res; + + _dbus_return_val_if_fail (server != NULL, NULL); + + SERVER_LOCK (server); + + res = _dbus_data_slot_list_get (&slot_allocator, + &server->slot_list, + slot); + + SERVER_UNLOCK (server); + + return res; +} + +/** @} */ + +#ifdef DBUS_BUILD_TESTS +#include "dbus-test.h" +#include + +dbus_bool_t +_dbus_server_test (void) +{ + const char *valid_addresses[] = { + "tcp:port=1234", + "tcp:host=localhost,port=1234", + "tcp:host=localhost,port=1234;tcp:port=5678", +#ifdef DBUS_UNIX + "unix:path=./boogie", + "tcp:port=1234;unix:path=./boogie", +#endif + }; + + DBusServer *server; + int i; + + for (i = 0; i < _DBUS_N_ELEMENTS (valid_addresses); i++) + { + DBusError error = DBUS_ERROR_INIT; + char *address; + char *id; + + server = dbus_server_listen (valid_addresses[i], &error); + if (server == NULL) + { + _dbus_warn ("server listen error: %s: %s\n", error.name, error.message); + dbus_error_free (&error); + _dbus_assert_not_reached ("Failed to listen for valid address."); + } + + id = dbus_server_get_id (server); + _dbus_assert (id != NULL); + address = dbus_server_get_address (server); + _dbus_assert (address != NULL); + + if (strstr (address, id) == NULL) + { + _dbus_warn ("server id '%s' is not in the server address '%s'\n", + id, address); + _dbus_assert_not_reached ("bad server id or address"); + } + + dbus_free (id); + dbus_free (address); + + dbus_server_disconnect (server); + dbus_server_unref (server); + } + + return TRUE; +} + +#endif /* DBUS_BUILD_TESTS */ diff --git a/src/dbus/dbus-server.h b/src/dbus/dbus-server.h new file mode 100644 index 0000000..77f8788 --- /dev/null +++ b/src/dbus/dbus-server.h @@ -0,0 +1,91 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-server.h DBusServer object + * + * Copyright (C) 2002, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION) +#error "Only can be included directly, this file may disappear or change contents." +#endif + +#ifndef DBUS_SERVER_H +#define DBUS_SERVER_H + +#include +#include +#include +#include + +DBUS_BEGIN_DECLS + +/** + * @addtogroup DBusServer + * @{ + */ + +typedef struct DBusServer DBusServer; + +/** Called when a new connection to the server is available. Must reference and save the new + * connection, or close the new connection. Set with dbus_server_set_new_connection_function(). + */ +typedef void (* DBusNewConnectionFunction) (DBusServer *server, + DBusConnection *new_connection, + void *data); + +DBusServer* dbus_server_listen (const char *address, + DBusError *error); +DBusServer* dbus_server_ref (DBusServer *server); +void dbus_server_unref (DBusServer *server); +void dbus_server_disconnect (DBusServer *server); +dbus_bool_t dbus_server_get_is_connected (DBusServer *server); +char* dbus_server_get_address (DBusServer *server); +char* dbus_server_get_id (DBusServer *server); +void dbus_server_set_new_connection_function (DBusServer *server, + DBusNewConnectionFunction function, + void *data, + DBusFreeFunction free_data_function); +dbus_bool_t dbus_server_set_watch_functions (DBusServer *server, + DBusAddWatchFunction add_function, + DBusRemoveWatchFunction remove_function, + DBusWatchToggledFunction toggled_function, + void *data, + DBusFreeFunction free_data_function); +dbus_bool_t dbus_server_set_timeout_functions (DBusServer *server, + DBusAddTimeoutFunction add_function, + DBusRemoveTimeoutFunction remove_function, + DBusTimeoutToggledFunction toggled_function, + void *data, + DBusFreeFunction free_data_function); +dbus_bool_t dbus_server_set_auth_mechanisms (DBusServer *server, + const char **mechanisms); + +dbus_bool_t dbus_server_allocate_data_slot (dbus_int32_t *slot_p); +void dbus_server_free_data_slot (dbus_int32_t *slot_p); +dbus_bool_t dbus_server_set_data (DBusServer *server, + int slot, + void *data, + DBusFreeFunction free_data_func); +void* dbus_server_get_data (DBusServer *server, + int slot); + +/** @} */ + +DBUS_END_DECLS + +#endif /* DBUS_SERVER_H */ diff --git a/src/dbus/dbus-sha.c b/src/dbus/dbus-sha.c new file mode 100644 index 0000000..8ec50b6 --- /dev/null +++ b/src/dbus/dbus-sha.c @@ -0,0 +1,968 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-sha.c SHA-1 implementation + * + * Copyright (C) 2003 Red Hat Inc. + * Copyright (C) 1995 A. M. Kuchling + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-internals.h" +#include "dbus-sha.h" +#include "dbus-marshal-basic.h" /* for byteswap routines */ +#include + +/* The following comments have the history of where this code + * comes from. I actually copied it from GNet in GNOME CVS. + * - hp@redhat.com + */ + +/* + * sha.h : Implementation of the Secure Hash Algorithm + * + * Part of the Python Cryptography Toolkit, version 1.0.0 + * + * Copyright (C) 1995, A.M. Kuchling + * + * Distribute and use freely; there are no restrictions on further + * dissemination and usage except those imposed by the laws of your + * country of residence. + * + */ + +/* SHA: NIST's Secure Hash Algorithm */ + +/* Based on SHA code originally posted to sci.crypt by Peter Gutmann + in message <30ajo5$oe8@ccu2.auckland.ac.nz>. + Modified to test for endianness on creation of SHA objects by AMK. + Also, the original specification of SHA was found to have a weakness + by NSA/NIST. This code implements the fixed version of SHA. +*/ + +/* Here's the first paragraph of Peter Gutmann's posting: + +The following is my SHA (FIPS 180) code updated to allow use of the "fixed" +SHA, thanks to Jim Gillogly and an anonymous contributor for the information on +what's changed in the new version. The fix is a simple change which involves +adding a single rotate in the initial expansion function. It is unknown +whether this is an optimal solution to the problem which was discovered in the +SHA or whether it's simply a bandaid which fixes the problem with a minimum of +effort (for example the reengineering of a great many Capstone chips). +*/ + +/** + * @defgroup DBusSHA SHA implementation + * @ingroup DBusInternals + * @brief SHA-1 hash + * + * Types and functions related to computing SHA-1 hash. + */ + +/** + * @defgroup DBusSHAInternals SHA implementation details + * @ingroup DBusInternals + * @brief Internals of SHA implementation. + * + * The implementation of SHA-1 (see http://www.itl.nist.gov/fipspubs/fip180-1.htm). + * This SHA implementation was written by A.M. Kuchling + * + * @{ + */ + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +/* The SHA block size and message digest sizes, in bytes */ + +#define SHA_DATASIZE 64 +#define SHA_DIGESTSIZE 20 + +/* The SHA f()-functions. The f1 and f3 functions can be optimized to + save one boolean operation each - thanks to Rich Schroeppel, + rcs@cs.arizona.edu for discovering this */ + +/*#define f1(x,y,z) ( ( x & y ) | ( ~x & z ) ) // Rounds 0-19 */ +#define f1(x,y,z) ( z ^ ( x & ( y ^ z ) ) ) /* Rounds 0-19 */ +#define f2(x,y,z) ( x ^ y ^ z ) /* Rounds 20-39 */ +/*#define f3(x,y,z) ( ( x & y ) | ( x & z ) | ( y & z ) ) // Rounds 40-59 */ +#define f3(x,y,z) ( ( x & y ) | ( z & ( x | y ) ) ) /* Rounds 40-59 */ +#define f4(x,y,z) ( x ^ y ^ z ) /* Rounds 60-79 */ + +/* The SHA Mysterious Constants */ + +#define K1 0x5A827999L /* Rounds 0-19 */ +#define K2 0x6ED9EBA1L /* Rounds 20-39 */ +#define K3 0x8F1BBCDCL /* Rounds 40-59 */ +#define K4 0xCA62C1D6L /* Rounds 60-79 */ + +/* SHA initial values */ + +#define h0init 0x67452301L +#define h1init 0xEFCDAB89L +#define h2init 0x98BADCFEL +#define h3init 0x10325476L +#define h4init 0xC3D2E1F0L + +/* Note that it may be necessary to add parentheses to these macros if they + are to be called with expressions as arguments */ +/* 32-bit rotate left - kludged with shifts */ + +#define ROTL(n,X) ( ( ( X ) << n ) | ( ( X ) >> ( 32 - n ) ) ) + +/* The initial expanding function. The hash function is defined over an + 80-word expanded input array W, where the first 16 are copies of the input + data, and the remaining 64 are defined by + + W[ i ] = W[ i - 16 ] ^ W[ i - 14 ] ^ W[ i - 8 ] ^ W[ i - 3 ] + + This implementation generates these values on the fly in a circular + buffer - thanks to Colin Plumb, colin@nyx10.cs.du.edu for this + optimization. + + The updated SHA changes the expanding function by adding a rotate of 1 + bit. Thanks to Jim Gillogly, jim@rand.org, and an anonymous contributor + for this information */ + +#define expand(W,i) ( W[ i & 15 ] = ROTL( 1, ( W[ i & 15 ] ^ W[ (i - 14) & 15 ] ^ \ + W[ (i - 8) & 15 ] ^ W[ (i - 3) & 15 ] ) ) ) + + +/* The prototype SHA sub-round. The fundamental sub-round is: + + a' = e + ROTL( 5, a ) + f( b, c, d ) + k + data; + b' = a; + c' = ROTL( 30, b ); + d' = c; + e' = d; + + but this is implemented by unrolling the loop 5 times and renaming the + variables ( e, a, b, c, d ) = ( a', b', c', d', e' ) each iteration. + This code is then replicated 20 times for each of the 4 functions, using + the next 20 values from the W[] array each time */ + +#define subRound(a, b, c, d, e, f, k, data) \ + ( e += ROTL( 5, a ) + f( b, c, d ) + k + data, b = ROTL( 30, b ) ) + +#endif /* !DOXYGEN_SHOULD_SKIP_THIS */ + +/* Perform the SHA transformation. Note that this code, like MD5, seems to + break some optimizing compilers due to the complexity of the expressions + and the size of the basic block. It may be necessary to split it into + sections, e.g. based on the four subrounds + + Note that this corrupts the context->data area */ + +static void +SHATransform(dbus_uint32_t *digest, dbus_uint32_t *data) +{ + dbus_uint32_t A, B, C, D, E; /* Local vars */ + dbus_uint32_t eData[16]; /* Expanded data */ + + /* Set up first buffer and local data buffer */ + A = digest[0]; + B = digest[1]; + C = digest[2]; + D = digest[3]; + E = digest[4]; + memmove (eData, data, SHA_DATASIZE); + + /* Heavy mangling, in 4 sub-rounds of 20 interations each. */ + subRound (A, B, C, D, E, f1, K1, eData[0]); + subRound (E, A, B, C, D, f1, K1, eData[1]); + subRound (D, E, A, B, C, f1, K1, eData[2]); + subRound (C, D, E, A, B, f1, K1, eData[3]); + subRound (B, C, D, E, A, f1, K1, eData[4]); + subRound (A, B, C, D, E, f1, K1, eData[5]); + subRound (E, A, B, C, D, f1, K1, eData[6]); + subRound (D, E, A, B, C, f1, K1, eData[7]); + subRound (C, D, E, A, B, f1, K1, eData[8]); + subRound (B, C, D, E, A, f1, K1, eData[9]); + subRound (A, B, C, D, E, f1, K1, eData[10]); + subRound (E, A, B, C, D, f1, K1, eData[11]); + subRound (D, E, A, B, C, f1, K1, eData[12]); + subRound (C, D, E, A, B, f1, K1, eData[13]); + subRound (B, C, D, E, A, f1, K1, eData[14]); + subRound (A, B, C, D, E, f1, K1, eData[15]); + subRound (E, A, B, C, D, f1, K1, expand ( eData, 16) ); + subRound (D, E, A, B, C, f1, K1, expand ( eData, 17) ); + subRound (C, D, E, A, B, f1, K1, expand ( eData, 18) ); + subRound (B, C, D, E, A, f1, K1, expand ( eData, 19) ); + + subRound (A, B, C, D, E, f2, K2, expand ( eData, 20) ); + subRound (E, A, B, C, D, f2, K2, expand ( eData, 21) ); + subRound (D, E, A, B, C, f2, K2, expand ( eData, 22) ); + subRound (C, D, E, A, B, f2, K2, expand ( eData, 23) ); + subRound (B, C, D, E, A, f2, K2, expand ( eData, 24) ); + subRound (A, B, C, D, E, f2, K2, expand ( eData, 25) ); + subRound (E, A, B, C, D, f2, K2, expand ( eData, 26) ); + subRound (D, E, A, B, C, f2, K2, expand ( eData, 27) ); + subRound (C, D, E, A, B, f2, K2, expand ( eData, 28) ); + subRound (B, C, D, E, A, f2, K2, expand ( eData, 29) ); + subRound (A, B, C, D, E, f2, K2, expand ( eData, 30) ); + subRound (E, A, B, C, D, f2, K2, expand ( eData, 31) ); + subRound (D, E, A, B, C, f2, K2, expand ( eData, 32) ); + subRound (C, D, E, A, B, f2, K2, expand ( eData, 33) ); + subRound (B, C, D, E, A, f2, K2, expand ( eData, 34) ); + subRound (A, B, C, D, E, f2, K2, expand ( eData, 35) ); + subRound (E, A, B, C, D, f2, K2, expand ( eData, 36) ); + subRound (D, E, A, B, C, f2, K2, expand ( eData, 37) ); + subRound (C, D, E, A, B, f2, K2, expand ( eData, 38) ); + subRound (B, C, D, E, A, f2, K2, expand ( eData, 39) ); + + subRound (A, B, C, D, E, f3, K3, expand ( eData, 40) ); + subRound (E, A, B, C, D, f3, K3, expand ( eData, 41) ); + subRound (D, E, A, B, C, f3, K3, expand ( eData, 42) ); + subRound (C, D, E, A, B, f3, K3, expand ( eData, 43) ); + subRound (B, C, D, E, A, f3, K3, expand ( eData, 44) ); + subRound (A, B, C, D, E, f3, K3, expand ( eData, 45) ); + subRound (E, A, B, C, D, f3, K3, expand ( eData, 46) ); + subRound (D, E, A, B, C, f3, K3, expand ( eData, 47) ); + subRound (C, D, E, A, B, f3, K3, expand ( eData, 48) ); + subRound (B, C, D, E, A, f3, K3, expand ( eData, 49) ); + subRound (A, B, C, D, E, f3, K3, expand ( eData, 50) ); + subRound (E, A, B, C, D, f3, K3, expand ( eData, 51) ); + subRound (D, E, A, B, C, f3, K3, expand ( eData, 52) ); + subRound (C, D, E, A, B, f3, K3, expand ( eData, 53) ); + subRound (B, C, D, E, A, f3, K3, expand ( eData, 54) ); + subRound (A, B, C, D, E, f3, K3, expand ( eData, 55) ); + subRound (E, A, B, C, D, f3, K3, expand ( eData, 56) ); + subRound (D, E, A, B, C, f3, K3, expand ( eData, 57) ); + subRound (C, D, E, A, B, f3, K3, expand ( eData, 58) ); + subRound (B, C, D, E, A, f3, K3, expand ( eData, 59) ); + + subRound (A, B, C, D, E, f4, K4, expand ( eData, 60) ); + subRound (E, A, B, C, D, f4, K4, expand ( eData, 61) ); + subRound (D, E, A, B, C, f4, K4, expand ( eData, 62) ); + subRound (C, D, E, A, B, f4, K4, expand ( eData, 63) ); + subRound (B, C, D, E, A, f4, K4, expand ( eData, 64) ); + subRound (A, B, C, D, E, f4, K4, expand ( eData, 65) ); + subRound (E, A, B, C, D, f4, K4, expand ( eData, 66) ); + subRound (D, E, A, B, C, f4, K4, expand ( eData, 67) ); + subRound (C, D, E, A, B, f4, K4, expand ( eData, 68) ); + subRound (B, C, D, E, A, f4, K4, expand ( eData, 69) ); + subRound (A, B, C, D, E, f4, K4, expand ( eData, 70) ); + subRound (E, A, B, C, D, f4, K4, expand ( eData, 71) ); + subRound (D, E, A, B, C, f4, K4, expand ( eData, 72) ); + subRound (C, D, E, A, B, f4, K4, expand ( eData, 73) ); + subRound (B, C, D, E, A, f4, K4, expand ( eData, 74) ); + subRound (A, B, C, D, E, f4, K4, expand ( eData, 75) ); + subRound (E, A, B, C, D, f4, K4, expand ( eData, 76) ); + subRound (D, E, A, B, C, f4, K4, expand ( eData, 77) ); + subRound (C, D, E, A, B, f4, K4, expand ( eData, 78) ); + subRound (B, C, D, E, A, f4, K4, expand ( eData, 79) ); + + /* Build message digest */ + digest[0] += A; + digest[1] += B; + digest[2] += C; + digest[3] += D; + digest[4] += E; +} + +/* When run on a little-endian CPU we need to perform byte reversal on an + array of longwords. */ + +#ifdef WORDS_BIGENDIAN +#define swap_words(buffer, byte_count) +#else +static void +swap_words (dbus_uint32_t *buffer, + int byte_count) +{ + byte_count /= sizeof (dbus_uint32_t); + while (byte_count--) + { + *buffer = DBUS_UINT32_SWAP_LE_BE (*buffer); + ++buffer; + } +} +#endif + +static void +sha_init (DBusSHAContext *context) +{ + /* Set the h-vars to their initial values */ + context->digest[0] = h0init; + context->digest[1] = h1init; + context->digest[2] = h2init; + context->digest[3] = h3init; + context->digest[4] = h4init; + + /* Initialise bit count */ + context->count_lo = context->count_hi = 0; +} + +static void +sha_append (DBusSHAContext *context, + const unsigned char *buffer, + unsigned int count) +{ + dbus_uint32_t tmp; + unsigned int dataCount; + + /* Update bitcount */ + tmp = context->count_lo; + if (( context->count_lo = tmp + ( ( dbus_uint32_t) count << 3) ) < tmp) + context->count_hi++; /* Carry from low to high */ + context->count_hi += count >> 29; + + /* Get count of bytes already in data */ + dataCount = (int) (tmp >> 3) & 0x3F; + + /* Handle any leading odd-sized chunks */ + if (dataCount) + { + unsigned char *p = (unsigned char *) context->data + dataCount; + + dataCount = SHA_DATASIZE - dataCount; + if (count < dataCount) + { + memmove (p, buffer, count); + return; + } + memmove (p, buffer, dataCount); + swap_words (context->data, SHA_DATASIZE); + SHATransform (context->digest, context->data); + buffer += dataCount; + count -= dataCount; + } + + /* Process data in SHA_DATASIZE chunks */ + while (count >= SHA_DATASIZE) + { + memmove (context->data, buffer, SHA_DATASIZE); + swap_words (context->data, SHA_DATASIZE); + SHATransform (context->digest, context->data); + buffer += SHA_DATASIZE; + count -= SHA_DATASIZE; + } + + /* Handle any remaining bytes of data. */ + memmove (context->data, buffer, count); +} + + +/* Final wrapup - pad to SHA_DATASIZE-byte boundary with the bit pattern + 1 0* (64-bit count of bits processed, MSB-first) */ + +static void +sha_finish (DBusSHAContext *context, unsigned char digest[20]) +{ + int count; + unsigned char *data_p; + + /* Compute number of bytes mod 64 */ + count = (int) context->count_lo; + count = (count >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + data_p = (unsigned char *) context->data + count; + *data_p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = SHA_DATASIZE - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) + { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset (data_p, 0, count); + swap_words (context->data, SHA_DATASIZE); + SHATransform (context->digest, context->data); + + /* Now fill the next block with 56 bytes */ + memset (context->data, 0, SHA_DATASIZE - 8); + } + else + /* Pad block to 56 bytes */ + memset (data_p, 0, count - 8); + + /* Append length in bits and transform */ + context->data[14] = context->count_hi; + context->data[15] = context->count_lo; + + swap_words (context->data, SHA_DATASIZE - 8); + SHATransform (context->digest, context->data); + swap_words (context->digest, SHA_DIGESTSIZE); + memmove (digest, context->digest, SHA_DIGESTSIZE); +} + +/** @} */ /* End of internals */ + +/** + * @addtogroup DBusSHA + * + * @{ + */ + +/** + * Initializes the SHA context. + * + * @param context an uninitialized context, typically on the stack. + */ +void +_dbus_sha_init (DBusSHAContext *context) +{ + sha_init (context); +} + +/** + * Feeds more data into an existing shasum computation. + * + * @param context the SHA context + * @param data the additional data to hash + */ +void +_dbus_sha_update (DBusSHAContext *context, + const DBusString *data) +{ + unsigned int inputLen; + const unsigned char *input; + + input = (const unsigned char*) _dbus_string_get_const_data (data); + inputLen = _dbus_string_get_length (data); + + sha_append (context, input, inputLen); +} + +/** + * SHA finalization. Ends an SHA message-digest operation, writing the + * the message digest and zeroing the context. The results are + * returned as a raw 20-byte digest, not as the ascii-hex-digits + * string form of the digest. + * + * @param context the SHA context + * @param results string to append the 20-byte SHA digest to + * @returns #FALSE if not enough memory to append the digest + * + */ +dbus_bool_t +_dbus_sha_final (DBusSHAContext *context, + DBusString *results) +{ + unsigned char digest[20]; + + sha_finish (context, digest); + + if (!_dbus_string_append_len (results, digest, 20)) + return FALSE; + + /* some kind of security paranoia, though it seems pointless + * to me given the nonzeroed stuff flying around + */ + memset ((void*)context, '\0', sizeof (DBusSHAContext)); + + return TRUE; +} + +/** + * Computes the ASCII hex-encoded shasum of the given data and + * appends it to the output string. + * + * @param data input data to be hashed + * @param ascii_output string to append ASCII shasum to + * @returns #FALSE if not enough memory + */ +dbus_bool_t +_dbus_sha_compute (const DBusString *data, + DBusString *ascii_output) +{ + DBusSHAContext context; + DBusString digest; + + _dbus_sha_init (&context); + + _dbus_sha_update (&context, data); + + if (!_dbus_string_init (&digest)) + return FALSE; + + if (!_dbus_sha_final (&context, &digest)) + goto error; + + if (!_dbus_string_hex_encode (&digest, 0, ascii_output, + _dbus_string_get_length (ascii_output))) + goto error; + + _dbus_string_free (&digest); + + return TRUE; + + error: + _dbus_string_free (&digest); + return FALSE; +} + +/** @} */ /* end of exported functions */ + +#ifdef DBUS_BUILD_TESTS +#include "dbus-test.h" +#include + +static dbus_bool_t +check_sha_binary (const unsigned char *input, + int input_len, + const char *expected) +{ + DBusString input_str; + DBusString expected_str; + DBusString results; + + _dbus_string_init_const_len (&input_str, input, input_len); + _dbus_string_init_const (&expected_str, expected); + + if (!_dbus_string_init (&results)) + _dbus_assert_not_reached ("no memory for SHA-1 results"); + + if (!_dbus_sha_compute (&input_str, &results)) + _dbus_assert_not_reached ("no memory for SHA-1 results"); + + if (!_dbus_string_equal (&expected_str, &results)) + { + _dbus_warn ("Expected hash %s got %s for SHA-1 sum\n", + expected, + _dbus_string_get_const_data (&results)); + _dbus_string_free (&results); + return FALSE; + } + + _dbus_string_free (&results); + return TRUE; +} + +static dbus_bool_t +check_sha_str (const char *input, + const char *expected) +{ + return check_sha_binary (input, strlen (input), expected); +} + +static dbus_bool_t +decode_compact_string (const DBusString *line, + DBusString *decoded) +{ + int n_bits; + dbus_bool_t current_b; + int offset; + int next; + long val; + int length_bytes; + + offset = 0; + next = 0; + + if (!_dbus_string_parse_int (line, offset, &val, &next)) + { + fprintf (stderr, "could not parse length at start of compact string: %s\n", + _dbus_string_get_const_data (line)); + return FALSE; + } + + _dbus_string_skip_blank (line, next, &next); + + offset = next; + if (!_dbus_string_parse_int (line, offset, &val, &next)) + { + fprintf (stderr, "could not parse start bit 'b' in compact string: %s\n", + _dbus_string_get_const_data (line)); + return FALSE; + } + + if (!(val == 0 || val == 1)) + { + fprintf (stderr, "the value 'b' must be 0 or 1, see sha-1/Readme.txt\n"); + return FALSE; + } + + _dbus_string_skip_blank (line, next, &next); + + current_b = val; + n_bits = 0; + + while (next < _dbus_string_get_length (line)) + { + int total_bits; + + offset = next; + + if (_dbus_string_get_byte (line, offset) == '^') + break; + + if (!_dbus_string_parse_int (line, offset, &val, &next)) + { + fprintf (stderr, "could not parse bit count in compact string\n"); + return FALSE; + } + + /* We now append "val" copies of "current_b" bits to the string */ + total_bits = n_bits + val; + while (n_bits < total_bits) + { + int byte_containing_next_bit = n_bits / 8; + int bit_containing_next_bit = 7 - (n_bits % 8); + unsigned char old_byte; + + if (byte_containing_next_bit >= _dbus_string_get_length (decoded)) + { + if (!_dbus_string_set_length (decoded, byte_containing_next_bit + 1)) + _dbus_assert_not_reached ("no memory to extend to next byte"); + } + + old_byte = _dbus_string_get_byte (decoded, byte_containing_next_bit); + old_byte |= current_b << bit_containing_next_bit; + +#if 0 + printf ("Appending bit %d to byte %d at bit %d resulting in byte 0x%x\n", + current_b, byte_containing_next_bit, + bit_containing_next_bit, old_byte); +#endif + + _dbus_string_set_byte (decoded, byte_containing_next_bit, old_byte); + + ++n_bits; + } + + _dbus_string_skip_blank (line, next, &next); + + current_b = !current_b; + } + + length_bytes = (n_bits / 8 + ((n_bits % 8) ? 1 : 0)); + + if (_dbus_string_get_length (decoded) != length_bytes) + { + fprintf (stderr, "Expected length %d bytes %d bits for compact string, got %d bytes\n", + length_bytes, n_bits, _dbus_string_get_length (decoded)); + return FALSE; + } + else + return TRUE; +} + +static dbus_bool_t +get_next_expected_result (DBusString *results, + DBusString *result) +{ + DBusString line; + dbus_bool_t retval; + + retval = FALSE; + + if (!_dbus_string_init (&line)) + _dbus_assert_not_reached ("no memory"); + + next_iteration: + while (_dbus_string_pop_line (results, &line)) + { + _dbus_string_delete_leading_blanks (&line); + + if (_dbus_string_get_length (&line) == 0) + goto next_iteration; + else if (_dbus_string_starts_with_c_str (&line, "#")) + goto next_iteration; + else if (_dbus_string_starts_with_c_str (&line, "H>")) + { + /* don't print */ + } + else if (_dbus_string_starts_with_c_str (&line, "D>") || + _dbus_string_starts_with_c_str (&line, "")) + { + printf ("SHA-1: %s\n", _dbus_string_get_const_data (&line)); + + if (_dbus_string_find (&line, 0, "Type 3", NULL)) + { + /* See sha-1/Readme.txt - the "Type 3" tests are + * random seeds, rather than data to be hashed. + * we'd have to do a little bit more implementation + * to use those tests. + */ + + printf (" (ending tests due to Type 3 tests seen - this is normal)\n"); + break; + } + } + else if (_dbus_string_starts_with_c_str (&line, "D>") || + _dbus_string_starts_with_c_str (&line, " +#include +#include + +DBUS_BEGIN_DECLS + +typedef struct DBusSHAContext DBusSHAContext; + +/** + * Struct storing state of the SHA algorithm + */ +struct DBusSHAContext +{ + dbus_uint32_t digest[5]; /**< Message digest */ + dbus_uint32_t count_lo; /**< 64-bit bit count */ + dbus_uint32_t count_hi; /**< No clue */ + dbus_uint32_t data[16]; /**< SHA data buffer */ +}; + +void _dbus_sha_init (DBusSHAContext *context); +void _dbus_sha_update (DBusSHAContext *context, + const DBusString *data); +dbus_bool_t _dbus_sha_final (DBusSHAContext *context, + DBusString *results); +dbus_bool_t _dbus_sha_compute (const DBusString *data, + DBusString *ascii_output); + +DBUS_END_DECLS + +#endif /* DBUS_SHA_H */ diff --git a/src/dbus/dbus-shared.h b/src/dbus/dbus-shared.h new file mode 100644 index 0000000..b59ed34 --- /dev/null +++ b/src/dbus/dbus-shared.h @@ -0,0 +1,131 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-shared.h Stuff used by both dbus/dbus.h low-level and C/C++ binding APIs + * + * Copyright (C) 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef DBUS_SHARED_H +#define DBUS_SHARED_H + +/* Don't include anything in here from anywhere else. It's + * intended for use by any random library. + */ + +#ifdef __cplusplus +extern "C" { +#if 0 +} /* avoids confusing emacs indentation */ +#endif +#endif + +/* Normally docs are in .c files, but there isn't a .c file for this. */ +/** + * @defgroup DBusShared Shared constants + * @ingroup DBus + * + * @brief Shared header included by both libdbus and C/C++ bindings such as the GLib bindings. + * + * Usually a C/C++ binding such as the GLib or Qt binding won't want to include dbus.h in its + * public headers. However, a few constants and macros may be useful to include; those are + * found here and in dbus-protocol.h + * + * @{ + */ + + +/** + * Well-known bus types. See dbus_bus_get(). + */ +typedef enum +{ + DBUS_BUS_SESSION, /**< The login session bus */ + DBUS_BUS_SYSTEM, /**< The systemwide bus */ + DBUS_BUS_STARTER /**< The bus that started us, if any */ +} DBusBusType; + +/** + * Results that a message handler can return. + */ +typedef enum +{ + DBUS_HANDLER_RESULT_HANDLED, /**< Message has had its effect - no need to run more handlers. */ + DBUS_HANDLER_RESULT_NOT_YET_HANDLED, /**< Message has not had any effect - see if other handlers want it. */ + DBUS_HANDLER_RESULT_NEED_MEMORY /**< Need more memory in order to return #DBUS_HANDLER_RESULT_HANDLED or #DBUS_HANDLER_RESULT_NOT_YET_HANDLED. Please try again later with more memory. */ +} DBusHandlerResult; + +/* Bus names */ + +/** The bus name used to talk to the bus itself. */ +#define DBUS_SERVICE_DBUS "org.freedesktop.DBus" + +/* Paths */ +/** The object path used to talk to the bus itself. */ +#define DBUS_PATH_DBUS "/org/freedesktop/DBus" +/** The object path used in local/in-process-generated messages. */ +#define DBUS_PATH_LOCAL "/org/freedesktop/DBus/Local" + +/* Interfaces, these #define don't do much other than + * catch typos at compile time + */ +/** The interface exported by the object with #DBUS_SERVICE_DBUS and #DBUS_PATH_DBUS */ +#define DBUS_INTERFACE_DBUS "org.freedesktop.DBus" +/** The interface supported by introspectable objects */ +#define DBUS_INTERFACE_INTROSPECTABLE "org.freedesktop.DBus.Introspectable" +/** The interface supported by objects with properties */ +#define DBUS_INTERFACE_PROPERTIES "org.freedesktop.DBus.Properties" +/** The interface supported by most dbus peers */ +#define DBUS_INTERFACE_PEER "org.freedesktop.DBus.Peer" + +/** This is a special interface whose methods can only be invoked + * by the local implementation (messages from remote apps aren't + * allowed to specify this interface). + */ +#define DBUS_INTERFACE_LOCAL "org.freedesktop.DBus.Local" + +/* Owner flags */ +#define DBUS_NAME_FLAG_ALLOW_REPLACEMENT 0x1 /**< Allow another service to become the primary owner if requested */ +#define DBUS_NAME_FLAG_REPLACE_EXISTING 0x2 /**< Request to replace the current primary owner */ +#define DBUS_NAME_FLAG_DO_NOT_QUEUE 0x4 /**< If we can not become the primary owner do not place us in the queue */ + +/* Replies to request for a name */ +#define DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER 1 /**< Service has become the primary owner of the requested name */ +#define DBUS_REQUEST_NAME_REPLY_IN_QUEUE 2 /**< Service could not become the primary owner and has been placed in the queue */ +#define DBUS_REQUEST_NAME_REPLY_EXISTS 3 /**< Service is already in the queue */ +#define DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER 4 /**< Service is already the primary owner */ + +/* Replies to releasing a name */ +#define DBUS_RELEASE_NAME_REPLY_RELEASED 1 /**< Service was released from the given name */ +#define DBUS_RELEASE_NAME_REPLY_NON_EXISTENT 2 /**< The given name does not exist on the bus */ +#define DBUS_RELEASE_NAME_REPLY_NOT_OWNER 3 /**< Service is not an owner of the given name */ + +/* Replies to service starts */ +#define DBUS_START_REPLY_SUCCESS 1 /**< Service was auto started */ +#define DBUS_START_REPLY_ALREADY_RUNNING 2 /**< Service was already running */ + +/** @} */ + +#ifdef __cplusplus +#if 0 +{ /* avoids confusing emacs indentation */ +#endif +} +#endif + +#endif /* DBUS_SHARED_H */ diff --git a/src/dbus/dbus-shell.c b/src/dbus/dbus-shell.c new file mode 100644 index 0000000..65038b1 --- /dev/null +++ b/src/dbus/dbus-shell.c @@ -0,0 +1,640 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-shell.c Shell command line utility functions. + * + * Copyright (C) 2002, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include "dbus-internals.h" +#include "dbus-list.h" +#include "dbus-memory.h" +#include "dbus-protocol.h" +#include "dbus-shell.h" +#include "dbus-string.h" + +/* Single quotes preserve the literal string exactly. escape + * sequences are not allowed; not even \' - if you want a ' + * in the quoted text, you have to do something like 'foo'\''bar' + * + * Double quotes allow $ ` " \ and newline to be escaped with backslash. + * Otherwise double quotes preserve things literally. + */ + +static dbus_bool_t +unquote_string_inplace (char* str, char** end) +{ + char* dest; + char* s; + char quote_char; + + dest = s = str; + + quote_char = *s; + + if (!(*s == '"' || *s == '\'')) + { + *end = str; + return FALSE; + } + + /* Skip the initial quote mark */ + ++s; + + if (quote_char == '"') + { + while (*s) + { + _dbus_assert(s > dest); /* loop invariant */ + + switch (*s) + { + case '"': + /* End of the string, return now */ + *dest = '\0'; + ++s; + *end = s; + return TRUE; + + case '\\': + /* Possible escaped quote or \ */ + ++s; + switch (*s) + { + case '"': + case '\\': + case '`': + case '$': + case '\n': + *dest = *s; + ++s; + ++dest; + break; + + default: + /* not an escaped char */ + *dest = '\\'; + ++dest; + /* ++s already done. */ + break; + } + break; + + default: + *dest = *s; + ++dest; + ++s; + break; + } + + _dbus_assert(s > dest); /* loop invariant */ + } + } + else + { + while (*s) + { + _dbus_assert(s > dest); /* loop invariant */ + + if (*s == '\'') + { + /* End of the string, return now */ + *dest = '\0'; + ++s; + *end = s; + return TRUE; + } + else + { + *dest = *s; + ++dest; + ++s; + } + + _dbus_assert(s > dest); /* loop invariant */ + } + } + + /* If we reach here this means the close quote was never encountered */ + + *dest = '\0'; + + *end = s; + return FALSE; +} + +/** + * Unquotes a string as the shell (/bin/sh) would. Only handles + * quotes; if a string contains file globs, arithmetic operators, + * variables, backticks, redirections, or other special-to-the-shell + * features, the result will be different from the result a real shell + * would produce (the variables, backticks, etc. will be passed + * through literally instead of being expanded). This function is + * guaranteed to succeed if applied to the result of + * _dbus_shell_quote(). If it fails, it returns %NULL. + * The @quoted_string need not actually contain quoted or + * escaped text; _dbus_shell_unquote() simply goes through the string and + * unquotes/unescapes anything that the shell would. Both single and + * double quotes are handled, as are escapes including escaped + * newlines. The return value must be freed with dbus_free(). + * + * Shell quoting rules are a bit strange. Single quotes preserve the + * literal string exactly. escape sequences are not allowed; not even + * \' - if you want a ' in the quoted text, you have to do something + * like 'foo'\''bar'. Double quotes allow $, `, ", \, and newline to + * be escaped with backslash. Otherwise double quotes preserve things + * literally. + * + * @quoted_string: shell-quoted string + **/ +char* +_dbus_shell_unquote (const char *quoted_string) +{ + char *unquoted; + char *end; + char *start; + char *ret; + DBusString retval; + + unquoted = _dbus_strdup (quoted_string); + if (unquoted == NULL) + return NULL; + + start = unquoted; + end = unquoted; + if (!_dbus_string_init (&retval)) + { + dbus_free (unquoted); + return NULL; + } + + /* The loop allows cases such as + * "foo"blah blah'bar'woo foo"baz"la la la\'\''foo' + */ + while (*start) + { + /* Append all non-quoted chars, honoring backslash escape + */ + + while (*start && !(*start == '"' || *start == '\'')) + { + if (*start == '\\') + { + /* all characters can get escaped by backslash, + * except newline, which is removed if it follows + * a backslash outside of quotes + */ + + ++start; + if (*start) + { + if (*start != '\n') + { + if (!_dbus_string_append_byte (&retval, *start)) + goto error; + } + ++start; + } + } + else + { + if (!_dbus_string_append_byte (&retval, *start)) + goto error; + ++start; + } + } + + if (*start) + { + if (!unquote_string_inplace (start, &end)) + goto error; + else + { + if (!_dbus_string_append (&retval, start)) + goto error; + start = end; + } + } + } + + ret = _dbus_strdup (_dbus_string_get_data (&retval)); + if (!ret) + goto error; + + dbus_free (unquoted); + _dbus_string_free (&retval); + + return ret; + + error: + dbus_free (unquoted); + _dbus_string_free (&retval); + return NULL; +} + +/* _dbus_shell_parse_argv() does a semi-arbitrary weird subset of the way + * the shell parses a command line. We don't do variable expansion, + * don't understand that operators are tokens, don't do tilde expansion, + * don't do command substitution, no arithmetic expansion, IFS gets ignored, + * don't do filename globs, don't remove redirection stuff, etc. + * + * READ THE UNIX98 SPEC on "Shell Command Language" before changing + * the behavior of this code. + * + * Steps to parsing the argv string: + * + * - tokenize the string (but since we ignore operators, + * our tokenization may diverge from what the shell would do) + * note that tokenization ignores the internals of a quoted + * word and it always splits on spaces, not on IFS even + * if we used IFS. We also ignore "end of input indicator" + * (I guess this is control-D?) + * + * Tokenization steps, from UNIX98 with operator stuff removed, + * are: + * + * 1) "If the current character is backslash, single-quote or + * double-quote (\, ' or ") and it is not quoted, it will affect + * quoting for subsequent characters up to the end of the quoted + * text. The rules for quoting are as described in Quoting + * . During token recognition no substitutions will be actually + * performed, and the result token will contain exactly the + * characters that appear in the input (except for newline + * character joining), unmodified, including any embedded or + * enclosing quotes or substitution operators, between the quote + * mark and the end of the quoted text. The token will not be + * delimited by the end of the quoted field." + * + * 2) "If the current character is an unquoted newline character, + * the current token will be delimited." + * + * 3) "If the current character is an unquoted blank character, any + * token containing the previous character is delimited and the + * current character will be discarded." + * + * 4) "If the previous character was part of a word, the current + * character will be appended to that word." + * + * 5) "If the current character is a "#", it and all subsequent + * characters up to, but excluding, the next newline character + * will be discarded as a comment. The newline character that + * ends the line is not considered part of the comment. The + * "#" starts a comment only when it is at the beginning of a + * token. Since the search for the end-of-comment does not + * consider an escaped newline character specially, a comment + * cannot be continued to the next line." + * + * 6) "The current character will be used as the start of a new word." + * + * + * - for each token (word), perform portions of word expansion, namely + * field splitting (using default whitespace IFS) and quote + * removal. Field splitting may increase the number of words. + * Quote removal does not increase the number of words. + * + * "If the complete expansion appropriate for a word results in an + * empty field, that empty field will be deleted from the list of + * fields that form the completely expanded command, unless the + * original word contained single-quote or double-quote characters." + * - UNIX98 spec + * + * + */ + +static dbus_bool_t +delimit_token (DBusString *token, + DBusList **retval, + DBusError *error) +{ + char *str; + + str = _dbus_strdup (_dbus_string_get_data (token)); + if (!str) + { + _DBUS_SET_OOM (error); + return FALSE; + } + + if (!_dbus_list_append (retval, str)) + { + dbus_free (str); + _DBUS_SET_OOM (error); + return FALSE; + } + + return TRUE; +} + +static DBusList* +tokenize_command_line (const char *command_line, DBusError *error) +{ + char current_quote; + const char *p; + DBusString current_token; + DBusList *retval = NULL; + dbus_bool_t quoted;; + + current_quote = '\0'; + quoted = FALSE; + p = command_line; + + if (!_dbus_string_init (¤t_token)) + { + _DBUS_SET_OOM (error); + return NULL; + } + + while (*p) + { + if (current_quote == '\\') + { + if (*p == '\n') + { + /* we append nothing; backslash-newline become nothing */ + } + else + { + if (!_dbus_string_append_byte (¤t_token, '\\') || + !_dbus_string_append_byte (¤t_token, *p)) + { + _DBUS_SET_OOM (error); + goto error; + } + } + + current_quote = '\0'; + } + else if (current_quote == '#') + { + /* Discard up to and including next newline */ + while (*p && *p != '\n') + ++p; + + current_quote = '\0'; + + if (*p == '\0') + break; + } + else if (current_quote) + { + if (*p == current_quote && + /* check that it isn't an escaped double quote */ + !(current_quote == '"' && quoted)) + { + /* close the quote */ + current_quote = '\0'; + } + + /* Everything inside quotes, and the close quote, + * gets appended literally. + */ + + if (!_dbus_string_append_byte (¤t_token, *p)) + { + _DBUS_SET_OOM (error); + goto error; + } + } + else + { + switch (*p) + { + case '\n': + if (!delimit_token (¤t_token, &retval, error)) + goto error; + + _dbus_string_free (¤t_token); + + if (!_dbus_string_init (¤t_token)) + { + _DBUS_SET_OOM (error); + goto init_error; + } + + break; + + case ' ': + case '\t': + /* If the current token contains the previous char, delimit + * the current token. A nonzero length + * token should always contain the previous char. + */ + if (_dbus_string_get_length (¤t_token) > 0) + { + if (!delimit_token (¤t_token, &retval, error)) + goto error; + + _dbus_string_free (¤t_token); + + if (!_dbus_string_init (¤t_token)) + { + _DBUS_SET_OOM (error); + goto init_error; + } + + } + + /* discard all unquoted blanks (don't add them to a token) */ + break; + + + /* single/double quotes are appended to the token, + * escapes are maybe appended next time through the loop, + * comment chars are never appended. + */ + + case '\'': + case '"': + if (!_dbus_string_append_byte (¤t_token, *p)) + { + _DBUS_SET_OOM (error); + goto error; + } + + /* FALL THRU */ + + case '#': + case '\\': + current_quote = *p; + break; + + default: + /* Combines rules 4) and 6) - if we have a token, append to it, + * otherwise create a new token. + */ + if (!_dbus_string_append_byte (¤t_token, *p)) + { + _DBUS_SET_OOM (error); + goto error; + } + break; + } + } + + /* We need to count consecutive backslashes mod 2, + * to detect escaped doublequotes. + */ + if (*p != '\\') + quoted = FALSE; + else + quoted = !quoted; + + ++p; + } + + if (!delimit_token (¤t_token, &retval, error)) + goto error; + + if (current_quote) + { + dbus_set_error_const (error, DBUS_ERROR_INVALID_ARGS, "Unclosed quotes in command line"); + goto error; + } + + if (retval == NULL) + { + dbus_set_error_const (error, DBUS_ERROR_INVALID_ARGS, "No tokens found in command line"); + goto error; + } + + _dbus_string_free (¤t_token); + + return retval; + + error: + _dbus_string_free (¤t_token); + + init_error: + if (retval) + { + _dbus_list_foreach (&retval, (DBusForeachFunction) dbus_free, NULL); + _dbus_list_clear (&retval); + } + + return NULL; +} + +/** + * _dbus_shell_parse_argv: + * + * Parses a command line into an argument vector, in much the same way + * the shell would, but without many of the expansions the shell would + * perform (variable expansion, globs, operators, filename expansion, + * etc. are not supported). The results are defined to be the same as + * those you would get from a UNIX98 /bin/sh, as long as the input + * contains none of the unsupported shell expansions. If the input + * does contain such expansions, they are passed through + * literally. Free the returned vector with dbus_free_string_array(). + * + * @command_line: command line to parse + * @argcp: return location for number of args + * @argvp: return location for array of args + * @error: error information + **/ +dbus_bool_t +_dbus_shell_parse_argv (const char *command_line, + int *argcp, + char ***argvp, + DBusError *error) +{ + /* Code based on poptParseArgvString() from libpopt */ + int argc = 0; + char **argv = NULL; + DBusList *tokens = NULL; + int i; + DBusList *tmp_list; + + if (!command_line) + { + _dbus_verbose ("Command line is NULL\n"); + return FALSE; + } + + tokens = tokenize_command_line (command_line, error); + if (tokens == NULL) + { + _dbus_verbose ("No tokens for command line '%s'\n", command_line); + return FALSE; + } + + /* Because we can't have introduced any new blank space into the + * tokens (we didn't do any new expansions), we don't need to + * perform field splitting. If we were going to honor IFS or do any + * expansions, we would have to do field splitting on each word + * here. Also, if we were going to do any expansion we would need to + * remove any zero-length words that didn't contain quotes + * originally; but since there's no expansion we know all words have + * nonzero length, unless they contain quotes. + * + * So, we simply remove quotes, and don't do any field splitting or + * empty word removal, since we know there was no way to introduce + * such things. + */ + + argc = _dbus_list_get_length (&tokens); + argv = dbus_new (char *, argc + 1); + if (!argv) + { + _DBUS_SET_OOM (error); + goto error; + } + + i = 0; + tmp_list = tokens; + while (tmp_list) + { + argv[i] = _dbus_shell_unquote (tmp_list->data); + + if (!argv[i]) + { + int j; + for (j = 0; j < i; j++) + dbus_free(argv[j]); + + dbus_free (argv); + _DBUS_SET_OOM (error); + goto error; + } + + tmp_list = _dbus_list_get_next_link (&tokens, tmp_list); + ++i; + } + argv[argc] = NULL; + + _dbus_list_foreach (&tokens, (DBusForeachFunction) dbus_free, NULL); + _dbus_list_clear (&tokens); + + if (argcp) + *argcp = argc; + + if (argvp) + *argvp = argv; + else + dbus_free_string_array (argv); + + return TRUE; + + error: + _dbus_list_foreach (&tokens, (DBusForeachFunction) dbus_free, NULL); + _dbus_list_clear (&tokens); + + return FALSE; + +} diff --git a/src/dbus/dbus-shell.h b/src/dbus/dbus-shell.h new file mode 100644 index 0000000..5316d51 --- /dev/null +++ b/src/dbus/dbus-shell.h @@ -0,0 +1,41 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-shell.h Shell command line utility functions. + * + * Copyright (C) 2002, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef DBUS_SHELL_H +#define DBUS_SHELL_H + +DBUS_BEGIN_DECLS + +char* _dbus_shell_unquote (const char *quoted_string); +dbus_bool_t _dbus_shell_parse_argv (const char *command_line, + int *argcp, + char ***argvp, + DBusError *error); + +DBUS_END_DECLS + +#endif /* DBUS_SHELL_H */ + + diff --git a/src/dbus/dbus-signature.c b/src/dbus/dbus-signature.c new file mode 100644 index 0000000..c7f8d0e --- /dev/null +++ b/src/dbus/dbus-signature.c @@ -0,0 +1,542 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-signature.c Routines for reading recursive type signatures + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-signature.h" +#include "dbus-marshal-recursive.h" +#include "dbus-marshal-basic.h" +#include "dbus-internals.h" +#include "dbus-test.h" + +/** + * Implementation details of #DBusSignatureIter, all fields are private + */ +typedef struct +{ + const char *pos; /**< current position in the signature string */ + unsigned int finished : 1; /**< true if we are at the end iter */ + unsigned int in_array : 1; /**< true if we are a subiterator pointing to an array's element type */ +} DBusSignatureRealIter; + +/** macro that checks whether a typecode is a container type */ +#define TYPE_IS_CONTAINER(typecode) \ + ((typecode) == DBUS_TYPE_STRUCT || \ + (typecode) == DBUS_TYPE_DICT_ENTRY || \ + (typecode) == DBUS_TYPE_VARIANT || \ + (typecode) == DBUS_TYPE_ARRAY) + + +/** + * @defgroup DBusSignature Type signature parsing + * @ingroup DBus + * @brief Parsing D-Bus type signatures + * @{ + */ + +/** + * Initializes a #DBusSignatureIter for reading a type signature. This + * function is not safe to use on invalid signatures; be sure to + * validate potentially invalid signatures with dbus_signature_validate + * before using this function. + * + * @param iter pointer to an iterator to initialize + * @param signature the type signature + */ +void +dbus_signature_iter_init (DBusSignatureIter *iter, + const char *signature) +{ + DBusSignatureRealIter *real_iter = (DBusSignatureRealIter *) iter; + + real_iter->pos = signature; + real_iter->finished = FALSE; + real_iter->in_array = FALSE; +} + +/** + * Returns the current type pointed to by the iterator. + * If the iterator is pointing at a type code such as 's', then + * it will be returned directly. + * + * However, when the parser encounters a container type start + * character such as '(' for a structure, the corresponding type for + * the container will be returned, e.g. DBUS_TYPE_STRUCT, not '('. + * In this case, you should initialize a sub-iterator with + * dbus_signature_iter_recurse() to parse the container type. + * + * @param iter pointer to an iterator + * @returns current type (e.g. #DBUS_TYPE_STRING, #DBUS_TYPE_ARRAY) + */ +int +dbus_signature_iter_get_current_type (const DBusSignatureIter *iter) +{ + DBusSignatureRealIter *real_iter = (DBusSignatureRealIter *) iter; + + return _dbus_first_type_in_signature_c_str (real_iter->pos, 0); +} + +/** + * Returns the signature of the single complete type starting at the + * given iterator. + * + * For example, if the iterator is pointing at the start of "(ii)ii" + * (which is "a struct of two ints, followed by an int, followed by an + * int"), then "(ii)" would be returned. If the iterator is pointing at + * one of the "i" then just that "i" would be returned. + * + * @param iter pointer to an iterator + * @returns current signature; or #NULL if no memory. Should be freed with dbus_free() + */ +char * +dbus_signature_iter_get_signature (const DBusSignatureIter *iter) +{ + DBusSignatureRealIter *real_iter = (DBusSignatureRealIter *) iter; + DBusString str; + char *ret; + int pos; + + if (!_dbus_string_init (&str)) + return NULL; + + pos = 0; + _dbus_type_signature_next (real_iter->pos, &pos); + + if (!_dbus_string_append_len (&str, real_iter->pos, pos)) + return NULL; + if (!_dbus_string_steal_data (&str, &ret)) + ret = NULL; + _dbus_string_free (&str); + + return ret; +} + +/** + * Convenience function for returning the element type of an array; + * This function allows you to avoid initializing a sub-iterator and + * getting its current type. + * + * Undefined behavior results if you invoke this function when the + * current type of the iterator is not #DBUS_TYPE_ARRAY. + * + * @param iter pointer to an iterator + * @returns current array element type + */ +int +dbus_signature_iter_get_element_type (const DBusSignatureIter *iter) +{ + DBusSignatureRealIter *real_iter = (DBusSignatureRealIter *) iter; + + _dbus_return_val_if_fail (dbus_signature_iter_get_current_type (iter) == DBUS_TYPE_ARRAY, DBUS_TYPE_INVALID); + + return _dbus_first_type_in_signature_c_str (real_iter->pos, 1); +} + +/** + * Skip to the next value on this "level". e.g. the next field in a + * struct, the next value in an array. Returns #FALSE at the end of the + * current container. + * + * @param iter the iterator + * @returns FALSE if nothing more to read at or below this level + */ +dbus_bool_t +dbus_signature_iter_next (DBusSignatureIter *iter) +{ + DBusSignatureRealIter *real_iter = (DBusSignatureRealIter *) iter; + + if (real_iter->finished) + return FALSE; + else + { + int pos; + + if (real_iter->in_array) + { + real_iter->finished = TRUE; + return FALSE; + } + + pos = 0; + _dbus_type_signature_next (real_iter->pos, &pos); + real_iter->pos += pos; + + if (*real_iter->pos == DBUS_STRUCT_END_CHAR + || *real_iter->pos == DBUS_DICT_ENTRY_END_CHAR) + { + real_iter->finished = TRUE; + return FALSE; + } + + return *real_iter->pos != DBUS_TYPE_INVALID; + } +} + +/** + * Initialize a new iterator pointing to the first type in the current + * container. + * + * The results are undefined when calling this if the current type is + * a non-container (i.e. if dbus_type_is_container() returns #FALSE + * for the result of dbus_signature_iter_get_current_type()). + * + * @param iter the current interator + * @param subiter an iterator to initialize pointing to the first child + */ +void +dbus_signature_iter_recurse (const DBusSignatureIter *iter, + DBusSignatureIter *subiter) +{ + DBusSignatureRealIter *real_iter = (DBusSignatureRealIter *) iter; + DBusSignatureRealIter *real_sub_iter = (DBusSignatureRealIter *) subiter; + + _dbus_return_if_fail (dbus_type_is_container (dbus_signature_iter_get_current_type (iter))); + + *real_sub_iter = *real_iter; + real_sub_iter->in_array = FALSE; + real_sub_iter->pos++; + + if (dbus_signature_iter_get_current_type (iter) == DBUS_TYPE_ARRAY) + real_sub_iter->in_array = TRUE; +} + +/** + * Check a type signature for validity. Remember that #NULL can always + * be passed instead of a DBusError*, if you don't care about having + * an error name and message. + * + * @param signature a potentially invalid type signature + * @param error error return + * @returns #TRUE if signature is valid or #FALSE if an error is set + */ +dbus_bool_t +dbus_signature_validate (const char *signature, + DBusError *error) + +{ + DBusString str; + DBusValidity reason; + + _dbus_string_init_const (&str, signature); + reason = _dbus_validate_signature_with_reason (&str, 0, _dbus_string_get_length (&str)); + + if (reason == DBUS_VALID) + return TRUE; + else + { + dbus_set_error (error, DBUS_ERROR_INVALID_SIGNATURE, _dbus_validity_to_error_message (reason)); + return FALSE; + } +} + +/** + * Check that a type signature is both valid and contains exactly one + * complete type. "One complete type" means a single basic type, + * array, struct, or dictionary, though the struct or array may be + * arbitrarily recursive and complex. More than one complete type + * would mean for example "ii" or two integers in sequence. + * + * @param signature a potentially invalid type signature + * @param error error return + * @returns #TRUE if signature is valid and has exactly one complete type + */ +dbus_bool_t +dbus_signature_validate_single (const char *signature, + DBusError *error) +{ + DBusSignatureIter iter; + + if (!dbus_signature_validate (signature, error)) + return FALSE; + + dbus_signature_iter_init (&iter, signature); + if (dbus_signature_iter_get_current_type (&iter) == DBUS_TYPE_INVALID) + goto lose; + if (!dbus_signature_iter_next (&iter)) + return TRUE; + lose: + dbus_set_error (error, DBUS_ERROR_INVALID_SIGNATURE, "Exactly one complete type required in signature"); + return FALSE; +} + +/** + * A "container type" can contain basic types, or nested + * container types. #DBUS_TYPE_INVALID is not a container type. + * + * This function will crash if passed a typecode that isn't + * in dbus-protocol.h + * + * @returns #TRUE if type is a container + */ +dbus_bool_t +dbus_type_is_container (int typecode) +{ + /* only reasonable (non-line-noise) typecodes are allowed */ + _dbus_return_val_if_fail (_dbus_type_is_valid (typecode) || typecode == DBUS_TYPE_INVALID, + FALSE); + return TYPE_IS_CONTAINER (typecode); +} + +/** + * A "basic type" is a somewhat arbitrary concept, but the intent is + * to include those types that are fully-specified by a single + * typecode, with no additional type information or nested values. So + * all numbers and strings are basic types and structs, arrays, and + * variants are not basic types. #DBUS_TYPE_INVALID is not a basic + * type. + * + * This function will crash if passed a typecode that isn't + * in dbus-protocol.h + * + * @returns #TRUE if type is basic + */ +dbus_bool_t +dbus_type_is_basic (int typecode) +{ + /* only reasonable (non-line-noise) typecodes are allowed */ + _dbus_return_val_if_fail (_dbus_type_is_valid (typecode) || typecode == DBUS_TYPE_INVALID, + FALSE); + + /* everything that isn't invalid or a container */ + return !(typecode == DBUS_TYPE_INVALID || TYPE_IS_CONTAINER (typecode)); +} + +/** + * Tells you whether values of this type can change length if you set + * them to some other value. For this purpose, you assume that the + * first byte of the old and new value would be in the same location, + * so alignment padding is not a factor. + * + * This function is useful to determine whether + * dbus_message_iter_get_fixed_array() may be used. + * + * Some structs are fixed-size (if they contain only fixed-size types) + * but struct is not considered a fixed type for purposes of this + * function. + * + * This function will crash if passed a typecode that isn't + * in dbus-protocol.h + * + * @returns #FALSE if the type can occupy different lengths + */ +dbus_bool_t +dbus_type_is_fixed (int typecode) +{ + /* only reasonable (non-line-noise) typecodes are allowed */ + _dbus_return_val_if_fail (_dbus_type_is_valid (typecode) || typecode == DBUS_TYPE_INVALID, + FALSE); + + switch (typecode) + { + case DBUS_TYPE_BYTE: + case DBUS_TYPE_BOOLEAN: + case DBUS_TYPE_INT16: + case DBUS_TYPE_UINT16: + case DBUS_TYPE_INT32: + case DBUS_TYPE_UINT32: + case DBUS_TYPE_INT64: + case DBUS_TYPE_UINT64: + case DBUS_TYPE_DOUBLE: + return TRUE; + default: + return FALSE; + } +} + +/** @} */ /* end of DBusSignature group */ + +#ifdef DBUS_BUILD_TESTS + +/** + * @ingroup DBusSignatureInternals + * Unit test for DBusSignature. + * + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_signature_test (void) +{ + DBusSignatureIter iter; + DBusSignatureIter subiter; + DBusSignatureIter subsubiter; + DBusSignatureIter subsubsubiter; + const char *sig; + dbus_bool_t boolres; + + _dbus_assert (sizeof (DBusSignatureIter) >= sizeof (DBusSignatureRealIter)); + + sig = ""; + _dbus_assert (dbus_signature_validate (sig, NULL)); + _dbus_assert (!dbus_signature_validate_single (sig, NULL)); + dbus_signature_iter_init (&iter, sig); + _dbus_assert (dbus_signature_iter_get_current_type (&iter) == DBUS_TYPE_INVALID); + + sig = DBUS_TYPE_STRING_AS_STRING; + _dbus_assert (dbus_signature_validate (sig, NULL)); + _dbus_assert (dbus_signature_validate_single (sig, NULL)); + dbus_signature_iter_init (&iter, sig); + _dbus_assert (dbus_signature_iter_get_current_type (&iter) == DBUS_TYPE_STRING); + + sig = DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_BYTE_AS_STRING; + _dbus_assert (dbus_signature_validate (sig, NULL)); + dbus_signature_iter_init (&iter, sig); + _dbus_assert (dbus_signature_iter_get_current_type (&iter) == DBUS_TYPE_STRING); + boolres = dbus_signature_iter_next (&iter); + _dbus_assert (boolres); + _dbus_assert (dbus_signature_iter_get_current_type (&iter) == DBUS_TYPE_BYTE); + + sig = DBUS_TYPE_UINT16_AS_STRING + DBUS_STRUCT_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_UINT32_AS_STRING + DBUS_TYPE_VARIANT_AS_STRING + DBUS_TYPE_DOUBLE_AS_STRING + DBUS_STRUCT_END_CHAR_AS_STRING; + _dbus_assert (dbus_signature_validate (sig, NULL)); + dbus_signature_iter_init (&iter, sig); + _dbus_assert (dbus_signature_iter_get_current_type (&iter) == DBUS_TYPE_UINT16); + boolres = dbus_signature_iter_next (&iter); + _dbus_assert (boolres); + _dbus_assert (dbus_signature_iter_get_current_type (&iter) == DBUS_TYPE_STRUCT); + dbus_signature_iter_recurse (&iter, &subiter); + _dbus_assert (dbus_signature_iter_get_current_type (&subiter) == DBUS_TYPE_STRING); + boolres = dbus_signature_iter_next (&subiter); + _dbus_assert (boolres); + _dbus_assert (dbus_signature_iter_get_current_type (&subiter) == DBUS_TYPE_UINT32); + boolres = dbus_signature_iter_next (&subiter); + _dbus_assert (boolres); + _dbus_assert (dbus_signature_iter_get_current_type (&subiter) == DBUS_TYPE_VARIANT); + boolres = dbus_signature_iter_next (&subiter); + _dbus_assert (boolres); + _dbus_assert (dbus_signature_iter_get_current_type (&subiter) == DBUS_TYPE_DOUBLE); + + sig = DBUS_TYPE_UINT16_AS_STRING + DBUS_STRUCT_BEGIN_CHAR_AS_STRING + DBUS_TYPE_UINT32_AS_STRING + DBUS_TYPE_BYTE_AS_STRING + DBUS_TYPE_ARRAY_AS_STRING + DBUS_TYPE_ARRAY_AS_STRING + DBUS_TYPE_DOUBLE_AS_STRING + DBUS_STRUCT_BEGIN_CHAR_AS_STRING + DBUS_TYPE_BYTE_AS_STRING + DBUS_STRUCT_END_CHAR_AS_STRING + DBUS_STRUCT_END_CHAR_AS_STRING; + _dbus_assert (dbus_signature_validate (sig, NULL)); + dbus_signature_iter_init (&iter, sig); + _dbus_assert (dbus_signature_iter_get_current_type (&iter) == DBUS_TYPE_UINT16); + boolres = dbus_signature_iter_next (&iter); + _dbus_assert (boolres); + _dbus_assert (dbus_signature_iter_get_current_type (&iter) == DBUS_TYPE_STRUCT); + dbus_signature_iter_recurse (&iter, &subiter); + _dbus_assert (dbus_signature_iter_get_current_type (&subiter) == DBUS_TYPE_UINT32); + boolres = dbus_signature_iter_next (&subiter); + _dbus_assert (boolres); + _dbus_assert (dbus_signature_iter_get_current_type (&subiter) == DBUS_TYPE_BYTE); + boolres = dbus_signature_iter_next (&subiter); + _dbus_assert (boolres); + _dbus_assert (dbus_signature_iter_get_current_type (&subiter) == DBUS_TYPE_ARRAY); + _dbus_assert (dbus_signature_iter_get_element_type (&subiter) == DBUS_TYPE_ARRAY); + + dbus_signature_iter_recurse (&subiter, &subsubiter); + _dbus_assert (dbus_signature_iter_get_current_type (&subsubiter) == DBUS_TYPE_ARRAY); + _dbus_assert (dbus_signature_iter_get_element_type (&subsubiter) == DBUS_TYPE_DOUBLE); + + dbus_signature_iter_recurse (&subsubiter, &subsubsubiter); + _dbus_assert (dbus_signature_iter_get_current_type (&subsubsubiter) == DBUS_TYPE_DOUBLE); + boolres = dbus_signature_iter_next (&subiter); + _dbus_assert (boolres); + _dbus_assert (dbus_signature_iter_get_current_type (&subiter) == DBUS_TYPE_STRUCT); + dbus_signature_iter_recurse (&subiter, &subsubiter); + _dbus_assert (dbus_signature_iter_get_current_type (&subsubiter) == DBUS_TYPE_BYTE); + + sig = DBUS_TYPE_ARRAY_AS_STRING + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_INT16_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING + DBUS_TYPE_VARIANT_AS_STRING; + _dbus_assert (dbus_signature_validate (sig, NULL)); + _dbus_assert (!dbus_signature_validate_single (sig, NULL)); + dbus_signature_iter_init (&iter, sig); + _dbus_assert (dbus_signature_iter_get_current_type (&iter) == DBUS_TYPE_ARRAY); + _dbus_assert (dbus_signature_iter_get_element_type (&iter) == DBUS_TYPE_DICT_ENTRY); + + dbus_signature_iter_recurse (&iter, &subiter); + dbus_signature_iter_recurse (&subiter, &subsubiter); + _dbus_assert (dbus_signature_iter_get_current_type (&subsubiter) == DBUS_TYPE_INT16); + boolres = dbus_signature_iter_next (&subsubiter); + _dbus_assert (boolres); + _dbus_assert (dbus_signature_iter_get_current_type (&subsubiter) == DBUS_TYPE_STRING); + boolres = dbus_signature_iter_next (&subsubiter); + _dbus_assert (!boolres); + + boolres = dbus_signature_iter_next (&iter); + _dbus_assert (boolres); + _dbus_assert (dbus_signature_iter_get_current_type (&iter) == DBUS_TYPE_VARIANT); + boolres = dbus_signature_iter_next (&iter); + _dbus_assert (!boolres); + + sig = DBUS_TYPE_DICT_ENTRY_AS_STRING; + _dbus_assert (!dbus_signature_validate (sig, NULL)); + + sig = DBUS_TYPE_ARRAY_AS_STRING; + _dbus_assert (!dbus_signature_validate (sig, NULL)); + + sig = DBUS_TYPE_UINT32_AS_STRING + DBUS_TYPE_ARRAY_AS_STRING; + _dbus_assert (!dbus_signature_validate (sig, NULL)); + + sig = DBUS_TYPE_ARRAY_AS_STRING + DBUS_TYPE_DICT_ENTRY_AS_STRING; + _dbus_assert (!dbus_signature_validate (sig, NULL)); + + sig = DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING; + _dbus_assert (!dbus_signature_validate (sig, NULL)); + + sig = DBUS_DICT_ENTRY_END_CHAR_AS_STRING; + _dbus_assert (!dbus_signature_validate (sig, NULL)); + + sig = DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_INT32_AS_STRING; + _dbus_assert (!dbus_signature_validate (sig, NULL)); + + sig = DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_INT32_AS_STRING + DBUS_TYPE_STRING_AS_STRING; + _dbus_assert (!dbus_signature_validate (sig, NULL)); + + sig = DBUS_STRUCT_END_CHAR_AS_STRING + DBUS_STRUCT_BEGIN_CHAR_AS_STRING; + _dbus_assert (!dbus_signature_validate (sig, NULL)); + + sig = DBUS_STRUCT_BEGIN_CHAR_AS_STRING + DBUS_TYPE_BOOLEAN_AS_STRING; + _dbus_assert (!dbus_signature_validate (sig, NULL)); + return TRUE; +#if 0 + oom: + _dbus_assert_not_reached ("out of memory"); + return FALSE; +#endif +} + +#endif + diff --git a/src/dbus/dbus-signature.h b/src/dbus/dbus-signature.h new file mode 100644 index 0000000..3739108 --- /dev/null +++ b/src/dbus/dbus-signature.h @@ -0,0 +1,81 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-signatures.h utility functions for D-Bus types + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION) +#error "Only can be included directly, this file may disappear or change contents." +#endif + +#ifndef DBUS_SIGNATURES_H +#define DBUS_SIGNATURES_H + +#include +#include +#include + +DBUS_BEGIN_DECLS + +/** + * @addtogroup DBusSignature + * @{ + */ + +/** + * DBusSignatureIter struct; contains no public fields + */ +typedef struct +{ + void *dummy1; /**< Don't use this */ + void *dummy2; /**< Don't use this */ + dbus_uint32_t dummy8; /**< Don't use this */ + int dummy12; /**< Don't use this */ + int dummy17; /**< Don't use this */ +} DBusSignatureIter; + +void dbus_signature_iter_init (DBusSignatureIter *iter, + const char *signature); + +int dbus_signature_iter_get_current_type (const DBusSignatureIter *iter); + +char * dbus_signature_iter_get_signature (const DBusSignatureIter *iter); + +int dbus_signature_iter_get_element_type (const DBusSignatureIter *iter); + +dbus_bool_t dbus_signature_iter_next (DBusSignatureIter *iter); + +void dbus_signature_iter_recurse (const DBusSignatureIter *iter, + DBusSignatureIter *subiter); + +dbus_bool_t dbus_signature_validate (const char *signature, + DBusError *error); + +dbus_bool_t dbus_signature_validate_single (const char *signature, + DBusError *error); + +dbus_bool_t dbus_type_is_basic (int typecode); +dbus_bool_t dbus_type_is_container (int typecode); +dbus_bool_t dbus_type_is_fixed (int typecode); + +/** @} */ + +DBUS_END_DECLS + +#endif /* DBUS_SIGNATURE_H */ diff --git a/src/dbus/dbus-spawn.c b/src/dbus/dbus-spawn.c new file mode 100644 index 0000000..f4e3b58 --- /dev/null +++ b/src/dbus/dbus-spawn.c @@ -0,0 +1,1457 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-spawn.c Wrapper around fork/exec + * + * Copyright (C) 2002, 2003, 2004 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include "dbus-spawn.h" +#include "dbus-sysdeps-unix.h" +#include "dbus-internals.h" +#include "dbus-test.h" +#include "dbus-protocol.h" + +#include +#include +#include +#include +#include +#ifdef HAVE_ERRNO_H +#include +#endif + +extern char **environ; + +/** + * @addtogroup DBusInternalsUtils + * @{ + */ + +/* + * I'm pretty sure this whole spawn file could be made simpler, + * if you thought about it a bit. + */ + +/** + * Enumeration for status of a read() + */ +typedef enum +{ + READ_STATUS_OK, /**< Read succeeded */ + READ_STATUS_ERROR, /**< Some kind of error */ + READ_STATUS_EOF /**< EOF returned */ +} ReadStatus; + +static ReadStatus +read_ints (int fd, + int *buf, + int n_ints_in_buf, + int *n_ints_read, + DBusError *error) +{ + size_t bytes = 0; + ReadStatus retval; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + retval = READ_STATUS_OK; + + while (TRUE) + { + size_t chunk; + ssize_t to_read; + + to_read = sizeof (int) * n_ints_in_buf - bytes; + + if (to_read == 0) + break; + + again: + + chunk = read (fd, + ((char*)buf) + bytes, + to_read); + + if (chunk < 0 && errno == EINTR) + goto again; + + if (chunk < 0) + { + dbus_set_error (error, + DBUS_ERROR_SPAWN_FAILED, + "Failed to read from child pipe (%s)", + _dbus_strerror (errno)); + + retval = READ_STATUS_ERROR; + break; + } + else if (chunk == 0) + { + retval = READ_STATUS_EOF; + break; /* EOF */ + } + else /* chunk > 0 */ + bytes += chunk; + } + + *n_ints_read = (int)(bytes / sizeof(int)); + + return retval; +} + +static ReadStatus +read_pid (int fd, + pid_t *buf, + DBusError *error) +{ + size_t bytes = 0; + ReadStatus retval; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + retval = READ_STATUS_OK; + + while (TRUE) + { + size_t chunk; + ssize_t to_read; + + to_read = sizeof (pid_t) - bytes; + + if (to_read == 0) + break; + + again: + + chunk = read (fd, + ((char*)buf) + bytes, + to_read); + if (chunk < 0 && errno == EINTR) + goto again; + + if (chunk < 0) + { + dbus_set_error (error, + DBUS_ERROR_SPAWN_FAILED, + "Failed to read from child pipe (%s)", + _dbus_strerror (errno)); + + retval = READ_STATUS_ERROR; + break; + } + else if (chunk == 0) + { + retval = READ_STATUS_EOF; + break; /* EOF */ + } + else /* chunk > 0 */ + bytes += chunk; + } + + return retval; +} + +/* The implementation uses an intermediate child between the main process + * and the grandchild. The grandchild is our spawned process. The intermediate + * child is a babysitter process; it keeps track of when the grandchild + * exits/crashes, and reaps the grandchild. + */ + +/* Messages from children to parents */ +enum +{ + CHILD_EXITED, /* This message is followed by the exit status int */ + CHILD_FORK_FAILED, /* Followed by errno */ + CHILD_EXEC_FAILED, /* Followed by errno */ + CHILD_PID /* Followed by pid_t */ +}; + +/** + * Babysitter implementation details + */ +struct DBusBabysitter +{ + int refcount; /**< Reference count */ + + char *executable; /**< executable name to use in error messages */ + + int socket_to_babysitter; /**< Connection to the babysitter process */ + int error_pipe_from_child; /**< Connection to the process that does the exec() */ + + pid_t sitter_pid; /**< PID Of the babysitter */ + pid_t grandchild_pid; /**< PID of the grandchild */ + + DBusWatchList *watches; /**< Watches */ + + DBusWatch *error_watch; /**< Error pipe watch */ + DBusWatch *sitter_watch; /**< Sitter pipe watch */ + + int errnum; /**< Error number */ + int status; /**< Exit status code */ + unsigned int have_child_status : 1; /**< True if child status has been reaped */ + unsigned int have_fork_errnum : 1; /**< True if we have an error code from fork() */ + unsigned int have_exec_errnum : 1; /**< True if we have an error code from exec() */ +}; + +static DBusBabysitter* +_dbus_babysitter_new (void) +{ + DBusBabysitter *sitter; + + sitter = dbus_new0 (DBusBabysitter, 1); + if (sitter == NULL) + return NULL; + + sitter->refcount = 1; + + sitter->socket_to_babysitter = -1; + sitter->error_pipe_from_child = -1; + + sitter->sitter_pid = -1; + sitter->grandchild_pid = -1; + + sitter->watches = _dbus_watch_list_new (); + if (sitter->watches == NULL) + goto failed; + + return sitter; + + failed: + _dbus_babysitter_unref (sitter); + return NULL; +} + +/** + * Increment the reference count on the babysitter object. + * + * @param sitter the babysitter + * @returns the babysitter + */ +DBusBabysitter * +_dbus_babysitter_ref (DBusBabysitter *sitter) +{ + _dbus_assert (sitter != NULL); + _dbus_assert (sitter->refcount > 0); + + sitter->refcount += 1; + + return sitter; +} + +/** + * Decrement the reference count on the babysitter object. + * When the reference count of the babysitter object reaches + * zero, the babysitter is killed and the child that was being + * babysat gets emancipated. + * + * @param sitter the babysitter + */ +void +_dbus_babysitter_unref (DBusBabysitter *sitter) +{ + _dbus_assert (sitter != NULL); + _dbus_assert (sitter->refcount > 0); + + sitter->refcount -= 1; + if (sitter->refcount == 0) + { + if (sitter->socket_to_babysitter >= 0) + { + /* If we haven't forked other babysitters + * since this babysitter and socket were + * created then this close will cause the + * babysitter to wake up from poll with + * a hangup and then the babysitter will + * quit itself. + */ + _dbus_close_socket (sitter->socket_to_babysitter, NULL); + sitter->socket_to_babysitter = -1; + } + + if (sitter->error_pipe_from_child >= 0) + { + _dbus_close_socket (sitter->error_pipe_from_child, NULL); + sitter->error_pipe_from_child = -1; + } + + if (sitter->sitter_pid > 0) + { + int status; + int ret; + + /* It's possible the babysitter died on its own above + * from the close, or was killed randomly + * by some other process, so first try to reap it + */ + ret = waitpid (sitter->sitter_pid, &status, WNOHANG); + + /* If we couldn't reap the child then kill it, and + * try again + */ + if (ret == 0) + kill (sitter->sitter_pid, SIGKILL); + + again: + if (ret == 0) + ret = waitpid (sitter->sitter_pid, &status, 0); + + if (ret < 0) + { + if (errno == EINTR) + goto again; + else if (errno == ECHILD) + _dbus_warn ("Babysitter process not available to be reaped; should not happen\n"); + else + _dbus_warn ("Unexpected error %d in waitpid() for babysitter: %s\n", + errno, _dbus_strerror (errno)); + } + else + { + _dbus_verbose ("Reaped %ld, waiting for babysitter %ld\n", + (long) ret, (long) sitter->sitter_pid); + + if (WIFEXITED (sitter->status)) + _dbus_verbose ("Babysitter exited with status %d\n", + WEXITSTATUS (sitter->status)); + else if (WIFSIGNALED (sitter->status)) + _dbus_verbose ("Babysitter received signal %d\n", + WTERMSIG (sitter->status)); + else + _dbus_verbose ("Babysitter exited abnormally\n"); + } + + sitter->sitter_pid = -1; + } + + if (sitter->error_watch) + { + _dbus_watch_invalidate (sitter->error_watch); + _dbus_watch_unref (sitter->error_watch); + sitter->error_watch = NULL; + } + + if (sitter->sitter_watch) + { + _dbus_watch_invalidate (sitter->sitter_watch); + _dbus_watch_unref (sitter->sitter_watch); + sitter->sitter_watch = NULL; + } + + if (sitter->watches) + _dbus_watch_list_free (sitter->watches); + + dbus_free (sitter->executable); + + dbus_free (sitter); + } +} + +static ReadStatus +read_data (DBusBabysitter *sitter, + int fd) +{ + int what; + int got; + DBusError error = DBUS_ERROR_INIT; + ReadStatus r; + + r = read_ints (fd, &what, 1, &got, &error); + + switch (r) + { + case READ_STATUS_ERROR: + _dbus_warn ("Failed to read data from fd %d: %s\n", fd, error.message); + dbus_error_free (&error); + return r; + + case READ_STATUS_EOF: + return r; + + case READ_STATUS_OK: + break; + } + + if (got == 1) + { + switch (what) + { + case CHILD_EXITED: + case CHILD_FORK_FAILED: + case CHILD_EXEC_FAILED: + { + int arg; + + r = read_ints (fd, &arg, 1, &got, &error); + + switch (r) + { + case READ_STATUS_ERROR: + _dbus_warn ("Failed to read arg from fd %d: %s\n", fd, error.message); + dbus_error_free (&error); + return r; + case READ_STATUS_EOF: + return r; + case READ_STATUS_OK: + break; + } + + if (got == 1) + { + if (what == CHILD_EXITED) + { + sitter->have_child_status = TRUE; + sitter->status = arg; + sitter->errnum = 0; + _dbus_verbose ("recorded child status exited = %d signaled = %d exitstatus = %d termsig = %d\n", + WIFEXITED (sitter->status), WIFSIGNALED (sitter->status), + WEXITSTATUS (sitter->status), WTERMSIG (sitter->status)); + } + else if (what == CHILD_FORK_FAILED) + { + sitter->have_fork_errnum = TRUE; + sitter->errnum = arg; + _dbus_verbose ("recorded fork errnum %d\n", sitter->errnum); + } + else if (what == CHILD_EXEC_FAILED) + { + sitter->have_exec_errnum = TRUE; + sitter->errnum = arg; + _dbus_verbose ("recorded exec errnum %d\n", sitter->errnum); + } + } + } + break; + case CHILD_PID: + { + pid_t pid = -1; + + r = read_pid (fd, &pid, &error); + + switch (r) + { + case READ_STATUS_ERROR: + _dbus_warn ("Failed to read PID from fd %d: %s\n", fd, error.message); + dbus_error_free (&error); + return r; + case READ_STATUS_EOF: + return r; + case READ_STATUS_OK: + break; + } + + sitter->grandchild_pid = pid; + + _dbus_verbose ("recorded grandchild pid %d\n", sitter->grandchild_pid); + } + break; + default: + _dbus_warn ("Unknown message received from babysitter process\n"); + break; + } + } + + return r; +} + +static void +close_socket_to_babysitter (DBusBabysitter *sitter) +{ + _dbus_verbose ("Closing babysitter\n"); + _dbus_close_socket (sitter->socket_to_babysitter, NULL); + sitter->socket_to_babysitter = -1; +} + +static void +close_error_pipe_from_child (DBusBabysitter *sitter) +{ + _dbus_verbose ("Closing child error\n"); + _dbus_close_socket (sitter->error_pipe_from_child, NULL); + sitter->error_pipe_from_child = -1; +} + +static void +handle_babysitter_socket (DBusBabysitter *sitter, + int revents) +{ + /* Even if we have POLLHUP, we want to keep reading + * data until POLLIN goes away; so this function only + * looks at HUP/ERR if no IN is set. + */ + if (revents & _DBUS_POLLIN) + { + _dbus_verbose ("Reading data from babysitter\n"); + if (read_data (sitter, sitter->socket_to_babysitter) != READ_STATUS_OK) + close_socket_to_babysitter (sitter); + } + else if (revents & (_DBUS_POLLERR | _DBUS_POLLHUP)) + { + close_socket_to_babysitter (sitter); + } +} + +static void +handle_error_pipe (DBusBabysitter *sitter, + int revents) +{ + if (revents & _DBUS_POLLIN) + { + _dbus_verbose ("Reading data from child error\n"); + if (read_data (sitter, sitter->error_pipe_from_child) != READ_STATUS_OK) + close_error_pipe_from_child (sitter); + } + else if (revents & (_DBUS_POLLERR | _DBUS_POLLHUP)) + { + close_error_pipe_from_child (sitter); + } +} + +/* returns whether there were any poll events handled */ +static dbus_bool_t +babysitter_iteration (DBusBabysitter *sitter, + dbus_bool_t block) +{ + DBusPollFD fds[2]; + int i; + dbus_bool_t descriptors_ready; + + descriptors_ready = FALSE; + + i = 0; + + if (sitter->error_pipe_from_child >= 0) + { + fds[i].fd = sitter->error_pipe_from_child; + fds[i].events = _DBUS_POLLIN; + fds[i].revents = 0; + ++i; + } + + if (sitter->socket_to_babysitter >= 0) + { + fds[i].fd = sitter->socket_to_babysitter; + fds[i].events = _DBUS_POLLIN; + fds[i].revents = 0; + ++i; + } + + if (i > 0) + { + int ret; + + do + { + ret = _dbus_poll (fds, i, 0); + } + while (ret < 0 && errno == EINTR); + + if (ret == 0 && block) + { + do + { + ret = _dbus_poll (fds, i, -1); + } + while (ret < 0 && errno == EINTR); + } + + if (ret > 0) + { + descriptors_ready = TRUE; + + while (i > 0) + { + --i; + if (fds[i].fd == sitter->error_pipe_from_child) + handle_error_pipe (sitter, fds[i].revents); + else if (fds[i].fd == sitter->socket_to_babysitter) + handle_babysitter_socket (sitter, fds[i].revents); + } + } + } + + return descriptors_ready; +} + +/** + * Macro returns #TRUE if the babysitter still has live sockets open to the + * babysitter child or the grandchild. + */ +#define LIVE_CHILDREN(sitter) ((sitter)->socket_to_babysitter >= 0 || (sitter)->error_pipe_from_child >= 0) + +/** + * Blocks until the babysitter process gives us the PID of the spawned grandchild, + * then kills the spawned grandchild. + * + * @param sitter the babysitter object + */ +void +_dbus_babysitter_kill_child (DBusBabysitter *sitter) +{ + /* be sure we have the PID of the child */ + while (LIVE_CHILDREN (sitter) && + sitter->grandchild_pid == -1) + babysitter_iteration (sitter, TRUE); + + _dbus_verbose ("Got child PID %ld for killing\n", + (long) sitter->grandchild_pid); + + if (sitter->grandchild_pid == -1) + return; /* child is already dead, or we're so hosed we'll never recover */ + + kill (sitter->grandchild_pid, SIGKILL); +} + +/** + * Checks whether the child has exited, without blocking. + * + * @param sitter the babysitter + */ +dbus_bool_t +_dbus_babysitter_get_child_exited (DBusBabysitter *sitter) +{ + + /* Be sure we're up-to-date */ + while (LIVE_CHILDREN (sitter) && + babysitter_iteration (sitter, FALSE)) + ; + + /* We will have exited the babysitter when the child has exited */ + return sitter->socket_to_babysitter < 0; +} + +/** + * Gets the exit status of the child. We do this so implementation specific + * detail is not cluttering up dbus, for example the system launcher code. + * This can only be called if the child has exited, i.e. call + * _dbus_babysitter_get_child_exited(). It returns FALSE if the child + * did not return a status code, e.g. because the child was signaled + * or we failed to ever launch the child in the first place. + * + * @param sitter the babysitter + * @param status the returned status code + * @returns #FALSE on failure + */ +dbus_bool_t +_dbus_babysitter_get_child_exit_status (DBusBabysitter *sitter, + int *status) +{ + if (!_dbus_babysitter_get_child_exited (sitter)) + _dbus_assert_not_reached ("Child has not exited"); + + if (!sitter->have_child_status || + !(WIFEXITED (sitter->status))) + return FALSE; + + *status = WEXITSTATUS (sitter->status); + return TRUE; +} + +/** + * Sets the #DBusError with an explanation of why the spawned + * child process exited (on a signal, or whatever). If + * the child process has not exited, does nothing (error + * will remain unset). + * + * @param sitter the babysitter + * @param error an error to fill in + */ +void +_dbus_babysitter_set_child_exit_error (DBusBabysitter *sitter, + DBusError *error) +{ + if (!_dbus_babysitter_get_child_exited (sitter)) + return; + + /* Note that if exec fails, we will also get a child status + * from the babysitter saying the child exited, + * so we need to give priority to the exec error + */ + if (sitter->have_exec_errnum) + { + dbus_set_error (error, DBUS_ERROR_SPAWN_EXEC_FAILED, + "Failed to execute program %s: %s", + sitter->executable, _dbus_strerror (sitter->errnum)); + } + else if (sitter->have_fork_errnum) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, + "Failed to fork a new process %s: %s", + sitter->executable, _dbus_strerror (sitter->errnum)); + } + else if (sitter->have_child_status) + { + if (WIFEXITED (sitter->status)) + dbus_set_error (error, DBUS_ERROR_SPAWN_CHILD_EXITED, + "Process %s exited with status %d", + sitter->executable, WEXITSTATUS (sitter->status)); + else if (WIFSIGNALED (sitter->status)) + dbus_set_error (error, DBUS_ERROR_SPAWN_CHILD_SIGNALED, + "Process %s received signal %d", + sitter->executable, WTERMSIG (sitter->status)); + else + dbus_set_error (error, DBUS_ERROR_FAILED, + "Process %s exited abnormally", + sitter->executable); + } + else + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Process %s exited, reason unknown", + sitter->executable); + } +} + +/** + * Sets watch functions to notify us when the + * babysitter object needs to read/write file descriptors. + * + * @param sitter the babysitter + * @param add_function function to begin monitoring a new descriptor. + * @param remove_function function to stop monitoring a descriptor. + * @param toggled_function function to notify when the watch is enabled/disabled + * @param data data to pass to add_function and remove_function. + * @param free_data_function function to be called to free the data. + * @returns #FALSE on failure (no memory) + */ +dbus_bool_t +_dbus_babysitter_set_watch_functions (DBusBabysitter *sitter, + DBusAddWatchFunction add_function, + DBusRemoveWatchFunction remove_function, + DBusWatchToggledFunction toggled_function, + void *data, + DBusFreeFunction free_data_function) +{ + return _dbus_watch_list_set_functions (sitter->watches, + add_function, + remove_function, + toggled_function, + data, + free_data_function); +} + +static dbus_bool_t +handle_watch (DBusWatch *watch, + unsigned int condition, + void *data) +{ + DBusBabysitter *sitter = data; + int revents; + int fd; + + revents = 0; + if (condition & DBUS_WATCH_READABLE) + revents |= _DBUS_POLLIN; + if (condition & DBUS_WATCH_ERROR) + revents |= _DBUS_POLLERR; + if (condition & DBUS_WATCH_HANGUP) + revents |= _DBUS_POLLHUP; + + fd = dbus_watch_get_socket (watch); + + if (fd == sitter->error_pipe_from_child) + handle_error_pipe (sitter, revents); + else if (fd == sitter->socket_to_babysitter) + handle_babysitter_socket (sitter, revents); + + while (LIVE_CHILDREN (sitter) && + babysitter_iteration (sitter, FALSE)) + ; + + return TRUE; +} + +/** Helps remember which end of the pipe is which */ +#define READ_END 0 +/** Helps remember which end of the pipe is which */ +#define WRITE_END 1 + + +/* Avoids a danger in threaded situations (calling close() + * on a file descriptor twice, and another thread has + * re-opened it since the first close) + */ +static int +close_and_invalidate (int *fd) +{ + int ret; + + if (*fd < 0) + return -1; + else + { + ret = _dbus_close_socket (*fd, NULL); + *fd = -1; + } + + return ret; +} + +static dbus_bool_t +make_pipe (int p[2], + DBusError *error) +{ + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + if (pipe (p) < 0) + { + dbus_set_error (error, + DBUS_ERROR_SPAWN_FAILED, + "Failed to create pipe for communicating with child process (%s)", + _dbus_strerror (errno)); + return FALSE; + } + + return TRUE; +} + +static void +do_write (int fd, const void *buf, size_t count) +{ + size_t bytes_written; + int ret; + + bytes_written = 0; + + again: + + ret = write (fd, ((const char*)buf) + bytes_written, count - bytes_written); + + if (ret < 0) + { + if (errno == EINTR) + goto again; + else + { + _dbus_warn ("Failed to write data to pipe!\n"); + exit (1); /* give up, we suck */ + } + } + else + bytes_written += ret; + + if (bytes_written < count) + goto again; +} + +static void +write_err_and_exit (int fd, int msg) +{ + int en = errno; + + do_write (fd, &msg, sizeof (msg)); + do_write (fd, &en, sizeof (en)); + + exit (1); +} + +static void +write_pid (int fd, pid_t pid) +{ + int msg = CHILD_PID; + + do_write (fd, &msg, sizeof (msg)); + do_write (fd, &pid, sizeof (pid)); +} + +static void +write_status_and_exit (int fd, int status) +{ + int msg = CHILD_EXITED; + + do_write (fd, &msg, sizeof (msg)); + do_write (fd, &status, sizeof (status)); + + exit (0); +} + +static void +do_exec (int child_err_report_fd, + char **argv, + char **envp, + DBusSpawnChildSetupFunc child_setup, + void *user_data) +{ +#ifdef DBUS_BUILD_TESTS + int i, max_open; +#endif + + _dbus_verbose_reset (); + _dbus_verbose ("Child process has PID " DBUS_PID_FORMAT "\n", + _dbus_getpid ()); + + if (child_setup) + (* child_setup) (user_data); + +#ifdef DBUS_BUILD_TESTS + max_open = sysconf (_SC_OPEN_MAX); + + for (i = 3; i < max_open; i++) + { + int retval; + + if (i == child_err_report_fd) + continue; + + retval = fcntl (i, F_GETFD); + + if (retval != -1 && !(retval & FD_CLOEXEC)) + _dbus_warn ("Fd %d did not have the close-on-exec flag set!\n", i); + } +#endif + + if (envp == NULL) + { + _dbus_assert (environ != NULL); + + envp = environ; + } + + execve (argv[0], argv, envp); + + /* Exec failed */ + write_err_and_exit (child_err_report_fd, + CHILD_EXEC_FAILED); +} + +static void +check_babysit_events (pid_t grandchild_pid, + int parent_pipe, + int revents) +{ + pid_t ret; + int status; + + do + { + ret = waitpid (grandchild_pid, &status, WNOHANG); + /* The man page says EINTR can't happen with WNOHANG, + * but there are reports of it (maybe only with valgrind?) + */ + } + while (ret < 0 && errno == EINTR); + + if (ret == 0) + { + _dbus_verbose ("no child exited\n"); + + ; /* no child exited */ + } + else if (ret < 0) + { + /* This isn't supposed to happen. */ + _dbus_warn ("unexpected waitpid() failure in check_babysit_events(): %s\n", + _dbus_strerror (errno)); + exit (1); + } + else if (ret == grandchild_pid) + { + /* Child exited */ + _dbus_verbose ("reaped child pid %ld\n", (long) ret); + + write_status_and_exit (parent_pipe, status); + } + else + { + _dbus_warn ("waitpid() reaped pid %d that we've never heard of\n", + (int) ret); + exit (1); + } + + if (revents & _DBUS_POLLIN) + { + _dbus_verbose ("babysitter got POLLIN from parent pipe\n"); + } + + if (revents & (_DBUS_POLLERR | _DBUS_POLLHUP)) + { + /* Parent is gone, so we just exit */ + _dbus_verbose ("babysitter got POLLERR or POLLHUP from parent\n"); + exit (0); + } +} + +static int babysit_sigchld_pipe = -1; + +static void +babysit_signal_handler (int signo) +{ + char b = '\0'; + again: + if (write (babysit_sigchld_pipe, &b, 1) <= 0) + if (errno == EINTR) + goto again; +} + +static void +babysit (pid_t grandchild_pid, + int parent_pipe) +{ + int sigchld_pipe[2]; + + /* We don't exec, so we keep parent state, such as the pid that + * _dbus_verbose() uses. Reset the pid here. + */ + _dbus_verbose_reset (); + + /* I thought SIGCHLD would just wake up the poll, but + * that didn't seem to work, so added this pipe. + * Probably the pipe is more likely to work on busted + * operating systems anyhow. + */ + if (pipe (sigchld_pipe) < 0) + { + _dbus_warn ("Not enough file descriptors to create pipe in babysitter process\n"); + exit (1); + } + + babysit_sigchld_pipe = sigchld_pipe[WRITE_END]; + + _dbus_set_signal_handler (SIGCHLD, babysit_signal_handler); + + write_pid (parent_pipe, grandchild_pid); + + check_babysit_events (grandchild_pid, parent_pipe, 0); + + while (TRUE) + { + DBusPollFD pfds[2]; + + pfds[0].fd = parent_pipe; + pfds[0].events = _DBUS_POLLIN; + pfds[0].revents = 0; + + pfds[1].fd = sigchld_pipe[READ_END]; + pfds[1].events = _DBUS_POLLIN; + pfds[1].revents = 0; + + if (_dbus_poll (pfds, _DBUS_N_ELEMENTS (pfds), -1) < 0 && errno != EINTR) + { + _dbus_warn ("_dbus_poll() error: %s\n", strerror (errno)); + exit (1); + } + + if (pfds[0].revents != 0) + { + check_babysit_events (grandchild_pid, parent_pipe, pfds[0].revents); + } + else if (pfds[1].revents & _DBUS_POLLIN) + { + char b; + read (sigchld_pipe[READ_END], &b, 1); + /* do waitpid check */ + check_babysit_events (grandchild_pid, parent_pipe, 0); + } + } + + exit (1); +} + +/** + * Spawns a new process. The executable name and argv[0] + * are the same, both are provided in argv[0]. The child_setup + * function is passed the given user_data and is run in the child + * just before calling exec(). + * + * Also creates a "babysitter" which tracks the status of the + * child process, advising the parent if the child exits. + * If the spawn fails, no babysitter is created. + * If sitter_p is #NULL, no babysitter is kept. + * + * @param sitter_p return location for babysitter or #NULL + * @param argv the executable and arguments + * @param env the environment (not used on unix yet) + * @param child_setup function to call in child pre-exec() + * @param user_data user data for setup function + * @param error error object to be filled in if function fails + * @returns #TRUE on success, #FALSE if error is filled in + */ +dbus_bool_t +_dbus_spawn_async_with_babysitter (DBusBabysitter **sitter_p, + char **argv, + char **env, + DBusSpawnChildSetupFunc child_setup, + void *user_data, + DBusError *error) +{ + DBusBabysitter *sitter; + int child_err_report_pipe[2] = { -1, -1 }; + int babysitter_pipe[2] = { -1, -1 }; + pid_t pid; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + if (sitter_p != NULL) + *sitter_p = NULL; + + sitter = NULL; + + sitter = _dbus_babysitter_new (); + if (sitter == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return FALSE; + } + + sitter->executable = _dbus_strdup (argv[0]); + if (sitter->executable == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto cleanup_and_fail; + } + + if (!make_pipe (child_err_report_pipe, error)) + goto cleanup_and_fail; + + _dbus_fd_set_close_on_exec (child_err_report_pipe[READ_END]); + _dbus_fd_set_close_on_exec (child_err_report_pipe[WRITE_END]); + + if (!_dbus_full_duplex_pipe (&babysitter_pipe[0], &babysitter_pipe[1], TRUE, error)) + goto cleanup_and_fail; + + _dbus_fd_set_close_on_exec (babysitter_pipe[0]); + _dbus_fd_set_close_on_exec (babysitter_pipe[1]); + + /* Setting up the babysitter is only useful in the parent, + * but we don't want to run out of memory and fail + * after we've already forked, since then we'd leak + * child processes everywhere. + */ + sitter->error_watch = _dbus_watch_new (child_err_report_pipe[READ_END], + DBUS_WATCH_READABLE, + TRUE, handle_watch, sitter, NULL); + if (sitter->error_watch == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto cleanup_and_fail; + } + + if (!_dbus_watch_list_add_watch (sitter->watches, sitter->error_watch)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto cleanup_and_fail; + } + + sitter->sitter_watch = _dbus_watch_new (babysitter_pipe[0], + DBUS_WATCH_READABLE, + TRUE, handle_watch, sitter, NULL); + if (sitter->sitter_watch == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto cleanup_and_fail; + } + + if (!_dbus_watch_list_add_watch (sitter->watches, sitter->sitter_watch)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto cleanup_and_fail; + } + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + pid = fork (); + + if (pid < 0) + { + dbus_set_error (error, + DBUS_ERROR_SPAWN_FORK_FAILED, + "Failed to fork (%s)", + _dbus_strerror (errno)); + goto cleanup_and_fail; + } + else if (pid == 0) + { + /* Immediate child, this is the babysitter process. */ + int grandchild_pid; + + /* Be sure we crash if the parent exits + * and we write to the err_report_pipe + */ + signal (SIGPIPE, SIG_DFL); + + /* Close the parent's end of the pipes. */ + close_and_invalidate (&child_err_report_pipe[READ_END]); + close_and_invalidate (&babysitter_pipe[0]); + + /* Create the child that will exec () */ + grandchild_pid = fork (); + + if (grandchild_pid < 0) + { + write_err_and_exit (babysitter_pipe[1], + CHILD_FORK_FAILED); + _dbus_assert_not_reached ("Got to code after write_err_and_exit()"); + } + else if (grandchild_pid == 0) + { + do_exec (child_err_report_pipe[WRITE_END], + argv, + env, + child_setup, user_data); + _dbus_assert_not_reached ("Got to code after exec() - should have exited on error"); + } + else + { + babysit (grandchild_pid, babysitter_pipe[1]); + _dbus_assert_not_reached ("Got to code after babysit()"); + } + } + else + { + /* Close the uncared-about ends of the pipes */ + close_and_invalidate (&child_err_report_pipe[WRITE_END]); + close_and_invalidate (&babysitter_pipe[1]); + + sitter->socket_to_babysitter = babysitter_pipe[0]; + babysitter_pipe[0] = -1; + + sitter->error_pipe_from_child = child_err_report_pipe[READ_END]; + child_err_report_pipe[READ_END] = -1; + + sitter->sitter_pid = pid; + + if (sitter_p != NULL) + *sitter_p = sitter; + else + _dbus_babysitter_unref (sitter); + + dbus_free_string_array (env); + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + return TRUE; + } + + cleanup_and_fail: + + _DBUS_ASSERT_ERROR_IS_SET (error); + + close_and_invalidate (&child_err_report_pipe[READ_END]); + close_and_invalidate (&child_err_report_pipe[WRITE_END]); + close_and_invalidate (&babysitter_pipe[0]); + close_and_invalidate (&babysitter_pipe[1]); + + if (sitter != NULL) + _dbus_babysitter_unref (sitter); + + return FALSE; +} + +/** @} */ + +#ifdef DBUS_BUILD_TESTS + +static void +_dbus_babysitter_block_for_child_exit (DBusBabysitter *sitter) +{ + while (LIVE_CHILDREN (sitter)) + babysitter_iteration (sitter, TRUE); +} + +static dbus_bool_t +check_spawn_nonexistent (void *data) +{ + char *argv[4] = { NULL, NULL, NULL, NULL }; + DBusBabysitter *sitter = NULL; + DBusError error = DBUS_ERROR_INIT; + + /*** Test launching nonexistent binary */ + + argv[0] = "/this/does/not/exist/32542sdgafgafdg"; + if (_dbus_spawn_async_with_babysitter (&sitter, argv, + NULL, NULL, NULL, + &error)) + { + _dbus_babysitter_block_for_child_exit (sitter); + _dbus_babysitter_set_child_exit_error (sitter, &error); + } + + if (sitter) + _dbus_babysitter_unref (sitter); + + if (!dbus_error_is_set (&error)) + { + _dbus_warn ("Did not get an error launching nonexistent executable\n"); + return FALSE; + } + + if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) || + dbus_error_has_name (&error, DBUS_ERROR_SPAWN_EXEC_FAILED))) + { + _dbus_warn ("Not expecting error when launching nonexistent executable: %s: %s\n", + error.name, error.message); + dbus_error_free (&error); + return FALSE; + } + + dbus_error_free (&error); + + return TRUE; +} + +static dbus_bool_t +check_spawn_segfault (void *data) +{ + char *argv[4] = { NULL, NULL, NULL, NULL }; + DBusBabysitter *sitter = NULL; + DBusError error = DBUS_ERROR_INIT; + + /*** Test launching segfault binary */ + + argv[0] = TEST_SEGFAULT_BINARY; + if (_dbus_spawn_async_with_babysitter (&sitter, argv, + NULL, NULL, NULL, + &error)) + { + _dbus_babysitter_block_for_child_exit (sitter); + _dbus_babysitter_set_child_exit_error (sitter, &error); + } + + if (sitter) + _dbus_babysitter_unref (sitter); + + if (!dbus_error_is_set (&error)) + { + _dbus_warn ("Did not get an error launching segfaulting binary\n"); + return FALSE; + } + + if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) || + dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_SIGNALED))) + { + _dbus_warn ("Not expecting error when launching segfaulting executable: %s: %s\n", + error.name, error.message); + dbus_error_free (&error); + return FALSE; + } + + dbus_error_free (&error); + + return TRUE; +} + +static dbus_bool_t +check_spawn_exit (void *data) +{ + char *argv[4] = { NULL, NULL, NULL, NULL }; + DBusBabysitter *sitter = NULL; + DBusError error = DBUS_ERROR_INIT; + + /*** Test launching exit failure binary */ + + argv[0] = TEST_EXIT_BINARY; + if (_dbus_spawn_async_with_babysitter (&sitter, argv, + NULL, NULL, NULL, + &error)) + { + _dbus_babysitter_block_for_child_exit (sitter); + _dbus_babysitter_set_child_exit_error (sitter, &error); + } + + if (sitter) + _dbus_babysitter_unref (sitter); + + if (!dbus_error_is_set (&error)) + { + _dbus_warn ("Did not get an error launching binary that exited with failure code\n"); + return FALSE; + } + + if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) || + dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED))) + { + _dbus_warn ("Not expecting error when launching exiting executable: %s: %s\n", + error.name, error.message); + dbus_error_free (&error); + return FALSE; + } + + dbus_error_free (&error); + + return TRUE; +} + +static dbus_bool_t +check_spawn_and_kill (void *data) +{ + char *argv[4] = { NULL, NULL, NULL, NULL }; + DBusBabysitter *sitter = NULL; + DBusError error = DBUS_ERROR_INIT; + + /*** Test launching sleeping binary then killing it */ + + argv[0] = TEST_SLEEP_FOREVER_BINARY; + if (_dbus_spawn_async_with_babysitter (&sitter, argv, + NULL, NULL, NULL, + &error)) + { + _dbus_babysitter_kill_child (sitter); + + _dbus_babysitter_block_for_child_exit (sitter); + + _dbus_babysitter_set_child_exit_error (sitter, &error); + } + + if (sitter) + _dbus_babysitter_unref (sitter); + + if (!dbus_error_is_set (&error)) + { + _dbus_warn ("Did not get an error after killing spawned binary\n"); + return FALSE; + } + + if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) || + dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_SIGNALED))) + { + _dbus_warn ("Not expecting error when killing executable: %s: %s\n", + error.name, error.message); + dbus_error_free (&error); + return FALSE; + } + + dbus_error_free (&error); + + return TRUE; +} + +dbus_bool_t +_dbus_spawn_test (const char *test_data_dir) +{ + if (!_dbus_test_oom_handling ("spawn_nonexistent", + check_spawn_nonexistent, + NULL)) + return FALSE; + + if (!_dbus_test_oom_handling ("spawn_segfault", + check_spawn_segfault, + NULL)) + return FALSE; + + if (!_dbus_test_oom_handling ("spawn_exit", + check_spawn_exit, + NULL)) + return FALSE; + + if (!_dbus_test_oom_handling ("spawn_and_kill", + check_spawn_and_kill, + NULL)) + return FALSE; + + return TRUE; +} +#endif diff --git a/src/dbus/dbus-spawn.h b/src/dbus/dbus-spawn.h new file mode 100644 index 0000000..7d8a67a --- /dev/null +++ b/src/dbus/dbus-spawn.h @@ -0,0 +1,61 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-spawn.h Wrapper around fork/exec + * + * Copyright (C) 2002, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef DBUS_SPAWN_H +#define DBUS_SPAWN_H + +#include +#include +#include + +DBUS_BEGIN_DECLS + +typedef void (* DBusSpawnChildSetupFunc) (void *user_data); + +typedef struct DBusBabysitter DBusBabysitter; + +dbus_bool_t _dbus_spawn_async_with_babysitter (DBusBabysitter **sitter_p, + char **argv, + char **env, + DBusSpawnChildSetupFunc child_setup, + void *user_data, + DBusError *error); +DBusBabysitter* _dbus_babysitter_ref (DBusBabysitter *sitter); +void _dbus_babysitter_unref (DBusBabysitter *sitter); +void _dbus_babysitter_kill_child (DBusBabysitter *sitter); +dbus_bool_t _dbus_babysitter_get_child_exited (DBusBabysitter *sitter); +void _dbus_babysitter_set_child_exit_error (DBusBabysitter *sitter, + DBusError *error); +dbus_bool_t _dbus_babysitter_get_child_exit_status (DBusBabysitter *sitter, + int *status); +dbus_bool_t _dbus_babysitter_set_watch_functions (DBusBabysitter *sitter, + DBusAddWatchFunction add_function, + DBusRemoveWatchFunction remove_function, + DBusWatchToggledFunction toggled_function, + void *data, + DBusFreeFunction free_data_function); + +DBUS_END_DECLS + +#endif /* DBUS_SPAWN_H */ diff --git a/src/dbus/dbus-string-private.h b/src/dbus/dbus-string-private.h new file mode 100644 index 0000000..16d7196 --- /dev/null +++ b/src/dbus/dbus-string-private.h @@ -0,0 +1,126 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-string-private.h String utility class (internal to D-Bus implementation) + * + * Copyright (C) 2002, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef DBUS_STRING_PRIVATE_H +#define DBUS_STRING_PRIVATE_H + +#include + +#include +#include + +#ifndef DBUS_CAN_USE_DBUS_STRING_PRIVATE +#error "Don't go including dbus-string-private.h for no good reason" +#endif + +DBUS_BEGIN_DECLS + +/** + * @brief Internals of DBusString. + * + * DBusString internals. DBusString is an opaque objects, it must be + * used via accessor functions. + */ +typedef struct +{ + unsigned char *str; /**< String data, plus nul termination */ + int len; /**< Length without nul */ + int allocated; /**< Allocated size of data */ + int max_length; /**< Max length of this string, without nul byte */ + unsigned int constant : 1; /**< String data is not owned by DBusString */ + unsigned int locked : 1; /**< DBusString has been locked and can't be changed */ + unsigned int invalid : 1; /**< DBusString is invalid (e.g. already freed) */ + unsigned int align_offset : 3; /**< str - align_offset is the actual malloc block */ +} DBusRealString; + + +/** + * @defgroup DBusStringInternals DBusString implementation details + * @ingroup DBusInternals + * @brief DBusString implementation details + * + * The guts of DBusString. + * + * @{ + */ + +/** + * This is the maximum max length (and thus also the maximum length) + * of a DBusString + */ +#define _DBUS_STRING_MAX_MAX_LENGTH (_DBUS_INT32_MAX - _DBUS_STRING_ALLOCATION_PADDING) + +/** + * Checks a bunch of assertions about a string object + * + * @param real the DBusRealString + */ +#define DBUS_GENERIC_STRING_PREAMBLE(real) _dbus_assert ((real) != NULL); _dbus_assert (!(real)->invalid); _dbus_assert ((real)->len >= 0); _dbus_assert ((real)->allocated >= 0); _dbus_assert ((real)->max_length >= 0); _dbus_assert ((real)->len <= ((real)->allocated - _DBUS_STRING_ALLOCATION_PADDING)); _dbus_assert ((real)->len <= (real)->max_length) + +/** + * Checks assertions about a string object that needs to be + * modifiable - may not be locked or const. Also declares + * the "real" variable pointing to DBusRealString. + * @param str the string + */ +#define DBUS_STRING_PREAMBLE(str) DBusRealString *real = (DBusRealString*) str; \ + DBUS_GENERIC_STRING_PREAMBLE (real); \ + _dbus_assert (!(real)->constant); \ + _dbus_assert (!(real)->locked) + +/** + * Checks assertions about a string object that may be locked but + * can't be const. i.e. a string object that we can free. Also + * declares the "real" variable pointing to DBusRealString. + * + * @param str the string + */ +#define DBUS_LOCKED_STRING_PREAMBLE(str) DBusRealString *real = (DBusRealString*) str; \ + DBUS_GENERIC_STRING_PREAMBLE (real); \ + _dbus_assert (!(real)->constant) + +/** + * Checks assertions about a string that may be const or locked. Also + * declares the "real" variable pointing to DBusRealString. + * @param str the string. + */ +#define DBUS_CONST_STRING_PREAMBLE(str) const DBusRealString *real = (DBusRealString*) str; \ + DBUS_GENERIC_STRING_PREAMBLE (real) + +/** + * Checks for ASCII blank byte + * @param c the byte + */ +#define DBUS_IS_ASCII_BLANK(c) ((c) == ' ' || (c) == '\t') + +/** + * Checks for ASCII whitespace byte + * @param c the byte + */ +#define DBUS_IS_ASCII_WHITE(c) ((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r') + +/** @} */ + +DBUS_END_DECLS + +#endif /* DBUS_STRING_PRIVATE_H */ diff --git a/src/dbus/dbus-string-util.c b/src/dbus/dbus-string-util.c new file mode 100644 index 0000000..aed9487 --- /dev/null +++ b/src/dbus/dbus-string-util.c @@ -0,0 +1,878 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-string-util.c Would be in dbus-string.c, but not used in libdbus + * + * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. + * Copyright (C) 2006 Ralf Habacker + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-internals.h" +#include "dbus-string.h" +#define DBUS_CAN_USE_DBUS_STRING_PRIVATE 1 +#include "dbus-string-private.h" + +/** + * @addtogroup DBusString + * @{ + */ + +/** + * Returns whether a string ends with the given suffix + * + * @todo memcmp might make this faster. + * + * @param a the string + * @param c_str the C-style string + * @returns #TRUE if the string ends with the suffix + */ +dbus_bool_t +_dbus_string_ends_with_c_str (const DBusString *a, + const char *c_str) +{ + const unsigned char *ap; + const unsigned char *bp; + const unsigned char *a_end; + unsigned long c_str_len; + const DBusRealString *real_a = (const DBusRealString*) a; + DBUS_GENERIC_STRING_PREAMBLE (real_a); + _dbus_assert (c_str != NULL); + + c_str_len = strlen (c_str); + if (((unsigned long)real_a->len) < c_str_len) + return FALSE; + + ap = real_a->str + (real_a->len - c_str_len); + bp = (const unsigned char*) c_str; + a_end = real_a->str + real_a->len; + while (ap != a_end) + { + if (*ap != *bp) + return FALSE; + + ++ap; + ++bp; + } + + _dbus_assert (*ap == '\0'); + _dbus_assert (*bp == '\0'); + + return TRUE; +} + +/** + * Find the given byte scanning backward from the given start. + * Sets *found to -1 if the byte is not found. + * + * @param str the string + * @param start the place to start scanning (will not find the byte at this point) + * @param byte the byte to find + * @param found return location for where it was found + * @returns #TRUE if found + */ +dbus_bool_t +_dbus_string_find_byte_backward (const DBusString *str, + int start, + unsigned char byte, + int *found) +{ + int i; + DBUS_CONST_STRING_PREAMBLE (str); + _dbus_assert (start <= real->len); + _dbus_assert (start >= 0); + _dbus_assert (found != NULL); + + i = start - 1; + while (i >= 0) + { + if (real->str[i] == byte) + break; + + --i; + } + + if (found) + *found = i; + + return i >= 0; +} + +/** @} */ + +#ifdef DBUS_BUILD_TESTS +#include "dbus-test.h" +#include + +static void +test_max_len (DBusString *str, + int max_len) +{ + if (max_len > 0) + { + if (!_dbus_string_set_length (str, max_len - 1)) + _dbus_assert_not_reached ("setting len to one less than max should have worked"); + } + + if (!_dbus_string_set_length (str, max_len)) + _dbus_assert_not_reached ("setting len to max len should have worked"); + + if (_dbus_string_set_length (str, max_len + 1)) + _dbus_assert_not_reached ("setting len to one more than max len should not have worked"); + + if (!_dbus_string_set_length (str, 0)) + _dbus_assert_not_reached ("setting len to zero should have worked"); +} + +static void +test_hex_roundtrip (const unsigned char *data, + int len) +{ + DBusString orig; + DBusString encoded; + DBusString decoded; + int end; + + if (len < 0) + len = strlen (data); + + if (!_dbus_string_init (&orig)) + _dbus_assert_not_reached ("could not init string"); + + if (!_dbus_string_init (&encoded)) + _dbus_assert_not_reached ("could not init string"); + + if (!_dbus_string_init (&decoded)) + _dbus_assert_not_reached ("could not init string"); + + if (!_dbus_string_append_len (&orig, data, len)) + _dbus_assert_not_reached ("couldn't append orig data"); + + if (!_dbus_string_hex_encode (&orig, 0, &encoded, 0)) + _dbus_assert_not_reached ("could not encode"); + + if (!_dbus_string_hex_decode (&encoded, 0, &end, &decoded, 0)) + _dbus_assert_not_reached ("could not decode"); + + _dbus_assert (_dbus_string_get_length (&encoded) == end); + + if (!_dbus_string_equal (&orig, &decoded)) + { + const char *s; + + printf ("Original string %d bytes encoded %d bytes decoded %d bytes\n", + _dbus_string_get_length (&orig), + _dbus_string_get_length (&encoded), + _dbus_string_get_length (&decoded)); + printf ("Original: %s\n", data); + s = _dbus_string_get_const_data (&decoded); + printf ("Decoded: %s\n", s); + _dbus_assert_not_reached ("original string not the same as string decoded from hex"); + } + + _dbus_string_free (&orig); + _dbus_string_free (&encoded); + _dbus_string_free (&decoded); +} + +typedef void (* TestRoundtripFunc) (const unsigned char *data, + int len); +static void +test_roundtrips (TestRoundtripFunc func) +{ + (* func) ("Hello this is a string\n", -1); + (* func) ("Hello this is a string\n1", -1); + (* func) ("Hello this is a string\n12", -1); + (* func) ("Hello this is a string\n123", -1); + (* func) ("Hello this is a string\n1234", -1); + (* func) ("Hello this is a string\n12345", -1); + (* func) ("", 0); + (* func) ("1", 1); + (* func) ("12", 2); + (* func) ("123", 3); + (* func) ("1234", 4); + (* func) ("12345", 5); + (* func) ("", 1); + (* func) ("1", 2); + (* func) ("12", 3); + (* func) ("123", 4); + (* func) ("1234", 5); + (* func) ("12345", 6); + { + unsigned char buf[512]; + int i; + + i = 0; + while (i < _DBUS_N_ELEMENTS (buf)) + { + buf[i] = i; + ++i; + } + i = 0; + while (i < _DBUS_N_ELEMENTS (buf)) + { + (* func) (buf, i); + ++i; + } + } +} + +#ifdef DBUS_BUILD_TESTS +/* The max length thing is sort of a historical artifact + * from a feature that turned out to be dumb; perhaps + * we should purge it entirely. The problem with + * the feature is that it looks like memory allocation + * failure, but is not a transient or resolvable failure. + */ +static void +set_max_length (DBusString *str, + int max_length) +{ + DBusRealString *real; + + real = (DBusRealString*) str; + + real->max_length = max_length; +} +#endif /* DBUS_BUILD_TESTS */ + +/** + * @ingroup DBusStringInternals + * Unit test for DBusString. + * + * @todo Need to write tests for _dbus_string_copy() and + * _dbus_string_move() moving to/from each of start/middle/end of a + * string. Also need tests for _dbus_string_move_len () + * + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_string_test (void) +{ + DBusString str; + DBusString other; + int i, end; + long v; + double d; + int lens[] = { 0, 1, 2, 3, 4, 5, 10, 16, 17, 18, 25, 31, 32, 33, 34, 35, 63, 64, 65, 66, 67, 68, 69, 70, 71, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136 }; + char *s; + dbus_unichar_t ch; + + i = 0; + while (i < _DBUS_N_ELEMENTS (lens)) + { + if (!_dbus_string_init (&str)) + _dbus_assert_not_reached ("failed to init string"); + + set_max_length (&str, lens[i]); + + test_max_len (&str, lens[i]); + _dbus_string_free (&str); + + ++i; + } + + /* Test shortening and setting length */ + i = 0; + while (i < _DBUS_N_ELEMENTS (lens)) + { + int j; + + if (!_dbus_string_init (&str)) + _dbus_assert_not_reached ("failed to init string"); + + set_max_length (&str, lens[i]); + + if (!_dbus_string_set_length (&str, lens[i])) + _dbus_assert_not_reached ("failed to set string length"); + + j = lens[i]; + while (j > 0) + { + _dbus_assert (_dbus_string_get_length (&str) == j); + if (j > 0) + { + _dbus_string_shorten (&str, 1); + _dbus_assert (_dbus_string_get_length (&str) == (j - 1)); + } + --j; + } + + _dbus_string_free (&str); + + ++i; + } + + /* Test equality */ + if (!_dbus_string_init (&str)) + _dbus_assert_not_reached ("oom"); + + if (!_dbus_string_append (&str, "Hello World")) + _dbus_assert_not_reached ("oom"); + + _dbus_string_init_const (&other, "H"); + _dbus_assert (_dbus_string_equal_substring (&str, 0, 1, &other, 0)); + _dbus_assert (_dbus_string_equal_substring (&str, 1, 0, &other, 1)); + _dbus_string_init_const (&other, "Hello"); + _dbus_assert (_dbus_string_equal_substring (&str, 0, 5, &other, 0)); + _dbus_assert (_dbus_string_equal_substring (&str, 1, 4, &other, 1)); + _dbus_assert (_dbus_string_equal_substring (&str, 2, 3, &other, 2)); + _dbus_assert (_dbus_string_equal_substring (&str, 3, 2, &other, 3)); + _dbus_assert (_dbus_string_equal_substring (&str, 4, 1, &other, 4)); + _dbus_assert (_dbus_string_equal_substring (&str, 5, 0, &other, 5)); + + _dbus_assert (_dbus_string_equal_substring (&other, 0, 5, &str, 0)); + _dbus_assert (_dbus_string_equal_substring (&other, 1, 4, &str, 1)); + _dbus_assert (_dbus_string_equal_substring (&other, 2, 3, &str, 2)); + _dbus_assert (_dbus_string_equal_substring (&other, 3, 2, &str, 3)); + _dbus_assert (_dbus_string_equal_substring (&other, 4, 1, &str, 4)); + _dbus_assert (_dbus_string_equal_substring (&other, 5, 0, &str, 5)); + + + _dbus_string_init_const (&other, "World"); + _dbus_assert (_dbus_string_equal_substring (&str, 6, 5, &other, 0)); + _dbus_assert (_dbus_string_equal_substring (&str, 7, 4, &other, 1)); + _dbus_assert (_dbus_string_equal_substring (&str, 8, 3, &other, 2)); + _dbus_assert (_dbus_string_equal_substring (&str, 9, 2, &other, 3)); + _dbus_assert (_dbus_string_equal_substring (&str, 10, 1, &other, 4)); + _dbus_assert (_dbus_string_equal_substring (&str, 11, 0, &other, 5)); + + _dbus_assert (_dbus_string_equal_substring (&other, 0, 5, &str, 6)); + _dbus_assert (_dbus_string_equal_substring (&other, 1, 4, &str, 7)); + _dbus_assert (_dbus_string_equal_substring (&other, 2, 3, &str, 8)); + _dbus_assert (_dbus_string_equal_substring (&other, 3, 2, &str, 9)); + _dbus_assert (_dbus_string_equal_substring (&other, 4, 1, &str, 10)); + _dbus_assert (_dbus_string_equal_substring (&other, 5, 0, &str, 11)); + + _dbus_string_free (&str); + + /* Test appending data */ + if (!_dbus_string_init (&str)) + _dbus_assert_not_reached ("failed to init string"); + + i = 0; + while (i < 10) + { + if (!_dbus_string_append (&str, "a")) + _dbus_assert_not_reached ("failed to append string to string\n"); + + _dbus_assert (_dbus_string_get_length (&str) == i * 2 + 1); + + if (!_dbus_string_append_byte (&str, 'b')) + _dbus_assert_not_reached ("failed to append byte to string\n"); + + _dbus_assert (_dbus_string_get_length (&str) == i * 2 + 2); + + ++i; + } + + _dbus_string_free (&str); + + /* Check steal_data */ + + if (!_dbus_string_init (&str)) + _dbus_assert_not_reached ("failed to init string"); + + if (!_dbus_string_append (&str, "Hello World")) + _dbus_assert_not_reached ("could not append to string"); + + i = _dbus_string_get_length (&str); + + if (!_dbus_string_steal_data (&str, &s)) + _dbus_assert_not_reached ("failed to steal data"); + + _dbus_assert (_dbus_string_get_length (&str) == 0); + _dbus_assert (((int)strlen (s)) == i); + + dbus_free (s); + + /* Check move */ + + if (!_dbus_string_append (&str, "Hello World")) + _dbus_assert_not_reached ("could not append to string"); + + i = _dbus_string_get_length (&str); + + if (!_dbus_string_init (&other)) + _dbus_assert_not_reached ("could not init string"); + + if (!_dbus_string_move (&str, 0, &other, 0)) + _dbus_assert_not_reached ("could not move"); + + _dbus_assert (_dbus_string_get_length (&str) == 0); + _dbus_assert (_dbus_string_get_length (&other) == i); + + if (!_dbus_string_append (&str, "Hello World")) + _dbus_assert_not_reached ("could not append to string"); + + if (!_dbus_string_move (&str, 0, &other, _dbus_string_get_length (&other))) + _dbus_assert_not_reached ("could not move"); + + _dbus_assert (_dbus_string_get_length (&str) == 0); + _dbus_assert (_dbus_string_get_length (&other) == i * 2); + + if (!_dbus_string_append (&str, "Hello World")) + _dbus_assert_not_reached ("could not append to string"); + + if (!_dbus_string_move (&str, 0, &other, _dbus_string_get_length (&other) / 2)) + _dbus_assert_not_reached ("could not move"); + + _dbus_assert (_dbus_string_get_length (&str) == 0); + _dbus_assert (_dbus_string_get_length (&other) == i * 3); + + _dbus_string_free (&other); + + /* Check copy */ + + if (!_dbus_string_append (&str, "Hello World")) + _dbus_assert_not_reached ("could not append to string"); + + i = _dbus_string_get_length (&str); + + if (!_dbus_string_init (&other)) + _dbus_assert_not_reached ("could not init string"); + + if (!_dbus_string_copy (&str, 0, &other, 0)) + _dbus_assert_not_reached ("could not copy"); + + _dbus_assert (_dbus_string_get_length (&str) == i); + _dbus_assert (_dbus_string_get_length (&other) == i); + + if (!_dbus_string_copy (&str, 0, &other, _dbus_string_get_length (&other))) + _dbus_assert_not_reached ("could not copy"); + + _dbus_assert (_dbus_string_get_length (&str) == i); + _dbus_assert (_dbus_string_get_length (&other) == i * 2); + _dbus_assert (_dbus_string_equal_c_str (&other, + "Hello WorldHello World")); + + if (!_dbus_string_copy (&str, 0, &other, _dbus_string_get_length (&other) / 2)) + _dbus_assert_not_reached ("could not copy"); + + _dbus_assert (_dbus_string_get_length (&str) == i); + _dbus_assert (_dbus_string_get_length (&other) == i * 3); + _dbus_assert (_dbus_string_equal_c_str (&other, + "Hello WorldHello WorldHello World")); + + _dbus_string_free (&str); + _dbus_string_free (&other); + + /* Check replace */ + + if (!_dbus_string_init (&str)) + _dbus_assert_not_reached ("failed to init string"); + + if (!_dbus_string_append (&str, "Hello World")) + _dbus_assert_not_reached ("could not append to string"); + + i = _dbus_string_get_length (&str); + + if (!_dbus_string_init (&other)) + _dbus_assert_not_reached ("could not init string"); + + if (!_dbus_string_replace_len (&str, 0, _dbus_string_get_length (&str), + &other, 0, _dbus_string_get_length (&other))) + _dbus_assert_not_reached ("could not replace"); + + _dbus_assert (_dbus_string_get_length (&str) == i); + _dbus_assert (_dbus_string_get_length (&other) == i); + _dbus_assert (_dbus_string_equal_c_str (&other, "Hello World")); + + if (!_dbus_string_replace_len (&str, 0, _dbus_string_get_length (&str), + &other, 5, 1)) + _dbus_assert_not_reached ("could not replace center space"); + + _dbus_assert (_dbus_string_get_length (&str) == i); + _dbus_assert (_dbus_string_get_length (&other) == i * 2 - 1); + _dbus_assert (_dbus_string_equal_c_str (&other, + "HelloHello WorldWorld")); + + + if (!_dbus_string_replace_len (&str, 1, 1, + &other, + _dbus_string_get_length (&other) - 1, + 1)) + _dbus_assert_not_reached ("could not replace end character"); + + _dbus_assert (_dbus_string_get_length (&str) == i); + _dbus_assert (_dbus_string_get_length (&other) == i * 2 - 1); + _dbus_assert (_dbus_string_equal_c_str (&other, + "HelloHello WorldWorle")); + + _dbus_string_free (&str); + _dbus_string_free (&other); + + /* Check append/get unichar */ + + if (!_dbus_string_init (&str)) + _dbus_assert_not_reached ("failed to init string"); + + ch = 0; + if (!_dbus_string_append_unichar (&str, 0xfffc)) + _dbus_assert_not_reached ("failed to append unichar"); + + _dbus_string_get_unichar (&str, 0, &ch, &i); + + _dbus_assert (ch == 0xfffc); + _dbus_assert (i == _dbus_string_get_length (&str)); + + _dbus_string_free (&str); + + /* Check insert/set/get byte */ + + if (!_dbus_string_init (&str)) + _dbus_assert_not_reached ("failed to init string"); + + if (!_dbus_string_append (&str, "Hello")) + _dbus_assert_not_reached ("failed to append Hello"); + + _dbus_assert (_dbus_string_get_byte (&str, 0) == 'H'); + _dbus_assert (_dbus_string_get_byte (&str, 1) == 'e'); + _dbus_assert (_dbus_string_get_byte (&str, 2) == 'l'); + _dbus_assert (_dbus_string_get_byte (&str, 3) == 'l'); + _dbus_assert (_dbus_string_get_byte (&str, 4) == 'o'); + + _dbus_string_set_byte (&str, 1, 'q'); + _dbus_assert (_dbus_string_get_byte (&str, 1) == 'q'); + + if (!_dbus_string_insert_bytes (&str, 0, 1, 255)) + _dbus_assert_not_reached ("can't insert byte"); + + if (!_dbus_string_insert_bytes (&str, 2, 4, 'Z')) + _dbus_assert_not_reached ("can't insert byte"); + + if (!_dbus_string_insert_bytes (&str, _dbus_string_get_length (&str), 1, 'W')) + _dbus_assert_not_reached ("can't insert byte"); + + _dbus_assert (_dbus_string_get_byte (&str, 0) == 255); + _dbus_assert (_dbus_string_get_byte (&str, 1) == 'H'); + _dbus_assert (_dbus_string_get_byte (&str, 2) == 'Z'); + _dbus_assert (_dbus_string_get_byte (&str, 3) == 'Z'); + _dbus_assert (_dbus_string_get_byte (&str, 4) == 'Z'); + _dbus_assert (_dbus_string_get_byte (&str, 5) == 'Z'); + _dbus_assert (_dbus_string_get_byte (&str, 6) == 'q'); + _dbus_assert (_dbus_string_get_byte (&str, 7) == 'l'); + _dbus_assert (_dbus_string_get_byte (&str, 8) == 'l'); + _dbus_assert (_dbus_string_get_byte (&str, 9) == 'o'); + _dbus_assert (_dbus_string_get_byte (&str, 10) == 'W'); + + _dbus_string_free (&str); + + /* Check append/parse int/double */ + + if (!_dbus_string_init (&str)) + _dbus_assert_not_reached ("failed to init string"); + + if (!_dbus_string_append_int (&str, 27)) + _dbus_assert_not_reached ("failed to append int"); + + i = _dbus_string_get_length (&str); + + if (!_dbus_string_parse_int (&str, 0, &v, &end)) + _dbus_assert_not_reached ("failed to parse int"); + + _dbus_assert (v == 27); + _dbus_assert (end == i); + + _dbus_string_free (&str); + + if (!_dbus_string_init (&str)) + _dbus_assert_not_reached ("failed to init string"); + + if (!_dbus_string_append_double (&str, 50.3)) + _dbus_assert_not_reached ("failed to append float"); + + i = _dbus_string_get_length (&str); + + if (!_dbus_string_parse_double (&str, 0, &d, &end)) + _dbus_assert_not_reached ("failed to parse float"); + + _dbus_assert (d > (50.3 - 1e-6) && d < (50.3 + 1e-6)); + _dbus_assert (end == i); + + _dbus_string_free (&str); + + /* Test find */ + if (!_dbus_string_init (&str)) + _dbus_assert_not_reached ("failed to init string"); + + if (!_dbus_string_append (&str, "Hello")) + _dbus_assert_not_reached ("couldn't append to string"); + + if (!_dbus_string_find (&str, 0, "He", &i)) + _dbus_assert_not_reached ("didn't find 'He'"); + _dbus_assert (i == 0); + + if (!_dbus_string_find (&str, 0, "Hello", &i)) + _dbus_assert_not_reached ("didn't find 'Hello'"); + _dbus_assert (i == 0); + + if (!_dbus_string_find (&str, 0, "ello", &i)) + _dbus_assert_not_reached ("didn't find 'ello'"); + _dbus_assert (i == 1); + + if (!_dbus_string_find (&str, 0, "lo", &i)) + _dbus_assert_not_reached ("didn't find 'lo'"); + _dbus_assert (i == 3); + + if (!_dbus_string_find (&str, 2, "lo", &i)) + _dbus_assert_not_reached ("didn't find 'lo'"); + _dbus_assert (i == 3); + + if (_dbus_string_find (&str, 4, "lo", &i)) + _dbus_assert_not_reached ("did find 'lo'"); + + if (!_dbus_string_find (&str, 0, "l", &i)) + _dbus_assert_not_reached ("didn't find 'l'"); + _dbus_assert (i == 2); + + if (!_dbus_string_find (&str, 0, "H", &i)) + _dbus_assert_not_reached ("didn't find 'H'"); + _dbus_assert (i == 0); + + if (!_dbus_string_find (&str, 0, "", &i)) + _dbus_assert_not_reached ("didn't find ''"); + _dbus_assert (i == 0); + + if (_dbus_string_find (&str, 0, "Hello!", NULL)) + _dbus_assert_not_reached ("Did find 'Hello!'"); + + if (_dbus_string_find (&str, 0, "Oh, Hello", NULL)) + _dbus_assert_not_reached ("Did find 'Oh, Hello'"); + + if (_dbus_string_find (&str, 0, "ill", NULL)) + _dbus_assert_not_reached ("Did find 'ill'"); + + if (_dbus_string_find (&str, 0, "q", NULL)) + _dbus_assert_not_reached ("Did find 'q'"); + + if (!_dbus_string_find_to (&str, 0, 2, "He", NULL)) + _dbus_assert_not_reached ("Didn't find 'He'"); + + if (_dbus_string_find_to (&str, 0, 2, "Hello", NULL)) + _dbus_assert_not_reached ("Did find 'Hello'"); + + if (!_dbus_string_find_byte_backward (&str, _dbus_string_get_length (&str), 'H', &i)) + _dbus_assert_not_reached ("Did not find 'H'"); + _dbus_assert (i == 0); + + if (!_dbus_string_find_byte_backward (&str, _dbus_string_get_length (&str), 'o', &i)) + _dbus_assert_not_reached ("Did not find 'o'"); + _dbus_assert (i == _dbus_string_get_length (&str) - 1); + + if (_dbus_string_find_byte_backward (&str, _dbus_string_get_length (&str) - 1, 'o', &i)) + _dbus_assert_not_reached ("Did find 'o'"); + _dbus_assert (i == -1); + + if (_dbus_string_find_byte_backward (&str, 1, 'e', &i)) + _dbus_assert_not_reached ("Did find 'e'"); + _dbus_assert (i == -1); + + if (!_dbus_string_find_byte_backward (&str, 2, 'e', &i)) + _dbus_assert_not_reached ("Didn't find 'e'"); + _dbus_assert (i == 1); + + _dbus_string_free (&str); + + /* Hex encoding */ + _dbus_string_init_const (&str, "cafebabe, this is a bogus hex string"); + if (!_dbus_string_init (&other)) + _dbus_assert_not_reached ("could not init string"); + + if (!_dbus_string_hex_decode (&str, 0, &end, &other, 0)) + _dbus_assert_not_reached ("deccoded bogus hex string with no error"); + + _dbus_assert (end == 8); + + _dbus_string_free (&other); + + test_roundtrips (test_hex_roundtrip); + + _dbus_string_free (&str); + + { + int found, found_len; + + _dbus_string_init_const (&str, "012\r\n567\n90"); + + if (!_dbus_string_find_eol (&str, 0, &found, &found_len) || found != 3 || found_len != 2) + _dbus_assert_not_reached ("Did not find '\\r\\n'"); + if (found != 3 || found_len != 2) + _dbus_assert_not_reached ("invalid return values"); + + if (!_dbus_string_find_eol (&str, 5, &found, &found_len)) + _dbus_assert_not_reached ("Did not find '\\n'"); + if (found != 8 || found_len != 1) + _dbus_assert_not_reached ("invalid return values"); + + if (_dbus_string_find_eol (&str, 9, &found, &found_len)) + _dbus_assert_not_reached ("Found not expected '\\n'"); + else if (found != 11 || found_len != 0) + _dbus_assert_not_reached ("invalid return values '\\n'"); + + found = -1; + found_len = -1; + _dbus_string_init_const (&str, ""); + if (_dbus_string_find_eol (&str, 0, &found, &found_len)) + _dbus_assert_not_reached ("found an eol in an empty string"); + _dbus_assert (found == 0); + _dbus_assert (found_len == 0); + + found = -1; + found_len = -1; + _dbus_string_init_const (&str, "foobar"); + if (_dbus_string_find_eol (&str, 0, &found, &found_len)) + _dbus_assert_not_reached ("found eol in string that lacks one"); + _dbus_assert (found == 6); + _dbus_assert (found_len == 0); + + found = -1; + found_len = -1; + _dbus_string_init_const (&str, "foobar\n"); + if (!_dbus_string_find_eol (&str, 0, &found, &found_len)) + _dbus_assert_not_reached ("did not find eol in string that has one at end"); + _dbus_assert (found == 6); + _dbus_assert (found_len == 1); + } + + { + DBusString line; + +#define FIRST_LINE "this is a line" +#define SECOND_LINE "this is a second line" + /* third line is empty */ +#define THIRD_LINE "" +#define FOURTH_LINE "this is a fourth line" + + if (!_dbus_string_init (&str)) + _dbus_assert_not_reached ("no memory"); + + if (!_dbus_string_append (&str, FIRST_LINE "\n" SECOND_LINE "\r\n" THIRD_LINE "\n" FOURTH_LINE)) + _dbus_assert_not_reached ("no memory"); + + if (!_dbus_string_init (&line)) + _dbus_assert_not_reached ("no memory"); + + if (!_dbus_string_pop_line (&str, &line)) + _dbus_assert_not_reached ("failed to pop first line"); + + _dbus_assert (_dbus_string_equal_c_str (&line, FIRST_LINE)); + + if (!_dbus_string_pop_line (&str, &line)) + _dbus_assert_not_reached ("failed to pop second line"); + + _dbus_assert (_dbus_string_equal_c_str (&line, SECOND_LINE)); + + if (!_dbus_string_pop_line (&str, &line)) + _dbus_assert_not_reached ("failed to pop third line"); + + _dbus_assert (_dbus_string_equal_c_str (&line, THIRD_LINE)); + + if (!_dbus_string_pop_line (&str, &line)) + _dbus_assert_not_reached ("failed to pop fourth line"); + + _dbus_assert (_dbus_string_equal_c_str (&line, FOURTH_LINE)); + + _dbus_string_free (&str); + _dbus_string_free (&line); + } + + { + if (!_dbus_string_init (&str)) + _dbus_assert_not_reached ("no memory"); + + for (i = 0; i < 10000; i++) + if (!_dbus_string_append (&str, "abcdefghijklmnopqrstuvwxyz")) + _dbus_assert_not_reached ("no memory"); + + if (!_dbus_string_set_length (&str, 10)) + _dbus_assert_not_reached ("failed to set length"); + + /* actually compact */ + if (!_dbus_string_compact (&str, 2048)) + _dbus_assert_not_reached ("failed to compact after set_length"); + + /* peek inside to make sure it worked */ + if (((DBusRealString *)&str)->allocated > 30) + _dbus_assert_not_reached ("compacting string didn't do anything"); + + if (!_dbus_string_equal_c_str (&str, "abcdefghij")) + _dbus_assert_not_reached ("unexpected content after compact"); + + /* compact nothing */ + if (!_dbus_string_compact (&str, 2048)) + _dbus_assert_not_reached ("failed to compact 2nd time"); + + if (!_dbus_string_equal_c_str (&str, "abcdefghij")) + _dbus_assert_not_reached ("unexpected content after 2nd compact"); + + /* and make sure it still works...*/ + if (!_dbus_string_append (&str, "123456")) + _dbus_assert_not_reached ("failed to append after compact"); + + if (!_dbus_string_equal_c_str (&str, "abcdefghij123456")) + _dbus_assert_not_reached ("unexpected content after append"); + + /* after growing automatically, this should do nothing */ + if (!_dbus_string_compact (&str, 20000)) + _dbus_assert_not_reached ("failed to compact after grow"); + + /* but this one will do something */ + if (!_dbus_string_compact (&str, 0)) + _dbus_assert_not_reached ("failed to compact after grow"); + + if (!_dbus_string_equal_c_str (&str, "abcdefghij123456")) + _dbus_assert_not_reached ("unexpected content"); + + if (!_dbus_string_append (&str, "!@#$%")) + _dbus_assert_not_reached ("failed to append after compact"); + + if (!_dbus_string_equal_c_str (&str, "abcdefghij123456!@#$%")) + _dbus_assert_not_reached ("unexpected content"); + + _dbus_string_free (&str); + } + + { + const char two_strings[] = "one\ttwo"; + + if (!_dbus_string_init (&str)) + _dbus_assert_not_reached ("no memory"); + + if (!_dbus_string_init (&other)) + _dbus_assert_not_reached ("no memory"); + + if (!_dbus_string_append (&str, two_strings)) + _dbus_assert_not_reached ("no memory"); + + if (!_dbus_string_split_on_byte (&str, '\t', &other)) + _dbus_assert_not_reached ("no memory or delimiter not found"); + + if (strcmp (_dbus_string_get_data (&str), "one") != 0) + _dbus_assert_not_reached ("left side after split on tab is wrong"); + + if (strcmp (_dbus_string_get_data (&other), "two") != 0) + _dbus_assert_not_reached ("right side after split on tab is wrong"); + + _dbus_string_free (&str); + _dbus_string_free (&other); + } + + return TRUE; +} + +#endif /* DBUS_BUILD_TESTS */ diff --git a/src/dbus/dbus-string.c b/src/dbus/dbus-string.c new file mode 100644 index 0000000..6b9b2bf --- /dev/null +++ b/src/dbus/dbus-string.c @@ -0,0 +1,2907 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-string.c String utility class (internal to D-Bus implementation) + * + * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. + * Copyright (C) 2006 Ralf Habacker + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-internals.h" +#include "dbus-string.h" +/* we allow a system header here, for speed/convenience */ +#include +/* for vsnprintf */ +#include +#define DBUS_CAN_USE_DBUS_STRING_PRIVATE 1 +#include "dbus-string-private.h" +#include "dbus-marshal-basic.h" /* probably should be removed by moving the usage of DBUS_TYPE + * into the marshaling-related files + */ +/* for DBUS_VA_COPY */ +#include "dbus-sysdeps.h" + +/** + * @defgroup DBusString DBusString class + * @ingroup DBusInternals + * @brief DBusString data structure for safer string handling + * + * Types and functions related to DBusString. DBusString is intended + * to be a string class that makes it hard to mess up security issues + * (and just in general harder to write buggy code). It should be + * used (or extended and then used) rather than the libc stuff in + * string.h. The string class is a bit inconvenient at spots because + * it handles out-of-memory failures and tries to be extra-robust. + * + * A DBusString has a maximum length set at initialization time; this + * can be used to ensure that a buffer doesn't get too big. The + * _dbus_string_lengthen() method checks for overflow, and for max + * length being exceeded. + * + * Try to avoid conversion to a plain C string, i.e. add methods on + * the string object instead, only convert to C string when passing + * things out to the public API. In particular, no sprintf, strcpy, + * strcat, any of that should be used. The GString feature of + * accepting negative numbers for "length of string" is also absent, + * because it could keep us from detecting bogus huge lengths. i.e. if + * we passed in some bogus huge length it would be taken to mean + * "current length of string" instead of "broken crack" + * + * @todo #DBusString needs a lot of cleaning up; some of the + * API is no longer used, and the API is pretty inconsistent. + * In particular all the "append" APIs, especially those involving + * alignment but probably lots of them, are no longer used by the + * marshaling code which always does "inserts" now. + */ + +/** + * @addtogroup DBusString + * @{ + */ + +static void +fixup_alignment (DBusRealString *real) +{ + unsigned char *aligned; + unsigned char *real_block; + unsigned int old_align_offset; + + /* we have to have extra space in real->allocated for the align offset and nul byte */ + _dbus_assert (real->len <= real->allocated - _DBUS_STRING_ALLOCATION_PADDING); + + old_align_offset = real->align_offset; + real_block = real->str - old_align_offset; + + aligned = _DBUS_ALIGN_ADDRESS (real_block, 8); + + real->align_offset = aligned - real_block; + real->str = aligned; + + if (old_align_offset != real->align_offset) + { + /* Here comes the suck */ + memmove (real_block + real->align_offset, + real_block + old_align_offset, + real->len + 1); + } + + _dbus_assert (real->align_offset < 8); + _dbus_assert (_DBUS_ALIGN_ADDRESS (real->str, 8) == real->str); +} + +static void +undo_alignment (DBusRealString *real) +{ + if (real->align_offset != 0) + { + memmove (real->str - real->align_offset, + real->str, + real->len + 1); + + real->str = real->str - real->align_offset; + real->align_offset = 0; + } +} + +/** + * Initializes a string that can be up to the given allocation size + * before it has to realloc. The string starts life with zero length. + * The string must eventually be freed with _dbus_string_free(). + * + * @param str memory to hold the string + * @param allocate_size amount to preallocate + * @returns #TRUE on success, #FALSE if no memory + */ +dbus_bool_t +_dbus_string_init_preallocated (DBusString *str, + int allocate_size) +{ + DBusRealString *real; + + _dbus_assert (str != NULL); + + _dbus_assert (sizeof (DBusString) == sizeof (DBusRealString)); + + real = (DBusRealString*) str; + + /* It's very important not to touch anything + * other than real->str if we're going to fail, + * since we also use this function to reset + * an existing string, e.g. in _dbus_string_steal_data() + */ + + real->str = dbus_malloc (_DBUS_STRING_ALLOCATION_PADDING + allocate_size); + if (real->str == NULL) + return FALSE; + + real->allocated = _DBUS_STRING_ALLOCATION_PADDING + allocate_size; + real->len = 0; + real->str[real->len] = '\0'; + + real->max_length = _DBUS_STRING_MAX_MAX_LENGTH; + real->constant = FALSE; + real->locked = FALSE; + real->invalid = FALSE; + real->align_offset = 0; + + fixup_alignment (real); + + return TRUE; +} + +/** + * Initializes a string. The string starts life with zero length. The + * string must eventually be freed with _dbus_string_free(). + * + * @param str memory to hold the string + * @returns #TRUE on success, #FALSE if no memory + */ +dbus_bool_t +_dbus_string_init (DBusString *str) +{ + return _dbus_string_init_preallocated (str, 0); +} + +#ifdef DBUS_BUILD_TESTS +/* The max length thing is sort of a historical artifact + * from a feature that turned out to be dumb; perhaps + * we should purge it entirely. The problem with + * the feature is that it looks like memory allocation + * failure, but is not a transient or resolvable failure. + */ +static void +set_max_length (DBusString *str, + int max_length) +{ + DBusRealString *real; + + real = (DBusRealString*) str; + + real->max_length = max_length; +} +#endif /* DBUS_BUILD_TESTS */ + +/** + * Initializes a constant string. The value parameter is not copied + * (should be static), and the string may never be modified. + * It is safe but not necessary to call _dbus_string_free() + * on a const string. The string has a length limit of MAXINT - 8. + * + * @param str memory to use for the string + * @param value a string to be stored in str (not copied!!!) + */ +void +_dbus_string_init_const (DBusString *str, + const char *value) +{ + _dbus_assert (value != NULL); + + _dbus_string_init_const_len (str, value, + strlen (value)); +} + +/** + * Initializes a constant string with a length. The value parameter is + * not copied (should be static), and the string may never be + * modified. It is safe but not necessary to call _dbus_string_free() + * on a const string. + * + * @param str memory to use for the string + * @param value a string to be stored in str (not copied!!!) + * @param len the length to use + */ +void +_dbus_string_init_const_len (DBusString *str, + const char *value, + int len) +{ + DBusRealString *real; + + _dbus_assert (str != NULL); + _dbus_assert (len == 0 || value != NULL); + _dbus_assert (len <= _DBUS_STRING_MAX_MAX_LENGTH); + _dbus_assert (len >= 0); + + real = (DBusRealString*) str; + + real->str = (unsigned char*) value; + real->len = len; + real->allocated = real->len + _DBUS_STRING_ALLOCATION_PADDING; /* a lie, just to avoid special-case assertions... */ + real->max_length = real->len + 1; + real->constant = TRUE; + real->locked = TRUE; + real->invalid = FALSE; + real->align_offset = 0; + + /* We don't require const strings to be 8-byte aligned as the + * memory is coming from elsewhere. + */ +} + +/** + * Frees a string created by _dbus_string_init(). + * + * @param str memory where the string is stored. + */ +void +_dbus_string_free (DBusString *str) +{ + DBusRealString *real = (DBusRealString*) str; + DBUS_GENERIC_STRING_PREAMBLE (real); + + if (real->constant) + return; + dbus_free (real->str - real->align_offset); + + real->invalid = TRUE; +} + +static dbus_bool_t +compact (DBusRealString *real, + int max_waste) +{ + unsigned char *new_str; + int new_allocated; + int waste; + + waste = real->allocated - (real->len + _DBUS_STRING_ALLOCATION_PADDING); + + if (waste <= max_waste) + return TRUE; + + new_allocated = real->len + _DBUS_STRING_ALLOCATION_PADDING; + + new_str = dbus_realloc (real->str - real->align_offset, new_allocated); + if (_DBUS_UNLIKELY (new_str == NULL)) + return FALSE; + + real->str = new_str + real->align_offset; + real->allocated = new_allocated; + fixup_alignment (real); + + return TRUE; +} + +#ifdef DBUS_BUILD_TESTS +/* Not using this feature at the moment, + * so marked DBUS_BUILD_TESTS-only + */ +/** + * Locks a string such that any attempts to change the string will + * result in aborting the program. Also, if the string is wasting a + * lot of memory (allocation is sufficiently larger than what the + * string is really using), _dbus_string_lock() will realloc the + * string's data to "compact" it. + * + * @param str the string to lock. + */ +void +_dbus_string_lock (DBusString *str) +{ + DBUS_LOCKED_STRING_PREAMBLE (str); /* can lock multiple times */ + + real->locked = TRUE; + + /* Try to realloc to avoid excess memory usage, since + * we know we won't change the string further + */ +#define MAX_WASTE 48 + compact (real, MAX_WASTE); +} +#endif /* DBUS_BUILD_TESTS */ + +static dbus_bool_t +reallocate_for_length (DBusRealString *real, + int new_length) +{ + int new_allocated; + unsigned char *new_str; + + /* at least double our old allocation to avoid O(n), avoiding + * overflow + */ + if (real->allocated > (_DBUS_STRING_MAX_MAX_LENGTH + _DBUS_STRING_ALLOCATION_PADDING) / 2) + new_allocated = _DBUS_STRING_MAX_MAX_LENGTH + _DBUS_STRING_ALLOCATION_PADDING; + else + new_allocated = real->allocated * 2; + + /* if you change the code just above here, run the tests without + * the following assert-only hack before you commit + */ + /* This is keyed off asserts in addition to tests so when you + * disable asserts to profile, you don't get this destroyer + * of profiles. + */ +#ifdef DBUS_DISABLE_ASSERT +#else +#ifdef DBUS_BUILD_TESTS + new_allocated = 0; /* ensure a realloc every time so that we go + * through all malloc failure codepaths + */ +#endif /* DBUS_BUILD_TESTS */ +#endif /* !DBUS_DISABLE_ASSERT */ + + /* But be sure we always alloc at least space for the new length */ + new_allocated = MAX (new_allocated, + new_length + _DBUS_STRING_ALLOCATION_PADDING); + + _dbus_assert (new_allocated >= real->allocated); /* code relies on this */ + new_str = dbus_realloc (real->str - real->align_offset, new_allocated); + if (_DBUS_UNLIKELY (new_str == NULL)) + return FALSE; + + real->str = new_str + real->align_offset; + real->allocated = new_allocated; + fixup_alignment (real); + + return TRUE; +} + +/** + * Compacts the string to avoid wasted memory. Wasted memory is + * memory that is allocated but not actually required to store the + * current length of the string. The compact is only done if more + * than the given amount of memory is being wasted (otherwise the + * waste is ignored and the call does nothing). + * + * @param str the string + * @param max_waste the maximum amount of waste to ignore + * @returns #FALSE if the compact failed due to realloc failure + */ +dbus_bool_t +_dbus_string_compact (DBusString *str, + int max_waste) +{ + DBUS_STRING_PREAMBLE (str); + + return compact (real, max_waste); +} + +static dbus_bool_t +set_length (DBusRealString *real, + int new_length) +{ + /* Note, we are setting the length not including nul termination */ + + /* exceeding max length is the same as failure to allocate memory */ + if (_DBUS_UNLIKELY (new_length > real->max_length)) + return FALSE; + else if (new_length > (real->allocated - _DBUS_STRING_ALLOCATION_PADDING) && + _DBUS_UNLIKELY (!reallocate_for_length (real, new_length))) + return FALSE; + else + { + real->len = new_length; + real->str[new_length] = '\0'; + return TRUE; + } +} + +static dbus_bool_t +open_gap (int len, + DBusRealString *dest, + int insert_at) +{ + if (len == 0) + return TRUE; + + if (len > dest->max_length - dest->len) + return FALSE; /* detected overflow of dest->len + len below */ + + if (!set_length (dest, dest->len + len)) + return FALSE; + + memmove (dest->str + insert_at + len, + dest->str + insert_at, + dest->len - len - insert_at); + + return TRUE; +} + +#ifndef _dbus_string_get_data +/** + * Gets the raw character buffer from the string. The returned buffer + * will be nul-terminated, but note that strings may contain binary + * data so there may be extra nul characters prior to the termination. + * This function should be little-used, extend DBusString or add + * stuff to dbus-sysdeps.c instead. It's an error to use this + * function on a const string. + * + * @param str the string + * @returns the data + */ +char* +_dbus_string_get_data (DBusString *str) +{ + DBUS_STRING_PREAMBLE (str); + + return (char*) real->str; +} +#endif /* _dbus_string_get_data */ + +/* only do the function if we don't have the macro */ +#ifndef _dbus_string_get_const_data +/** + * Gets the raw character buffer from a const string. + * + * @param str the string + * @returns the string data + */ +const char* +_dbus_string_get_const_data (const DBusString *str) +{ + DBUS_CONST_STRING_PREAMBLE (str); + + return (const char*) real->str; +} +#endif /* _dbus_string_get_const_data */ + +/** + * Gets a sub-portion of the raw character buffer from the + * string. The "len" field is required simply for error + * checking, to be sure you don't try to use more + * string than exists. The nul termination of the + * returned buffer remains at the end of the entire + * string, not at start + len. + * + * @param str the string + * @param start byte offset to return + * @param len length of segment to return + * @returns the string data + */ +char* +_dbus_string_get_data_len (DBusString *str, + int start, + int len) +{ + DBUS_STRING_PREAMBLE (str); + _dbus_assert (start >= 0); + _dbus_assert (len >= 0); + _dbus_assert (start <= real->len); + _dbus_assert (len <= real->len - start); + + return (char*) real->str + start; +} + +/* only do the function if we don't have the macro */ +#ifndef _dbus_string_get_const_data_len +/** + * const version of _dbus_string_get_data_len(). + * + * @param str the string + * @param start byte offset to return + * @param len length of segment to return + * @returns the string data + */ +const char* +_dbus_string_get_const_data_len (const DBusString *str, + int start, + int len) +{ + DBUS_CONST_STRING_PREAMBLE (str); + _dbus_assert (start >= 0); + _dbus_assert (len >= 0); + _dbus_assert (start <= real->len); + _dbus_assert (len <= real->len - start); + + return (const char*) real->str + start; +} +#endif /* _dbus_string_get_const_data_len */ + +/* only do the function if we don't have the macro */ +#ifndef _dbus_string_set_byte +/** + * Sets the value of the byte at the given position. + * + * @param str the string + * @param i the position + * @param byte the new value + */ +void +_dbus_string_set_byte (DBusString *str, + int i, + unsigned char byte) +{ + DBUS_STRING_PREAMBLE (str); + _dbus_assert (i < real->len); + _dbus_assert (i >= 0); + + real->str[i] = byte; +} +#endif /* _dbus_string_set_byte */ + +/* only have the function if we didn't create a macro */ +#ifndef _dbus_string_get_byte +/** + * Gets the byte at the given position. It is + * allowed to ask for the nul byte at the end of + * the string. + * + * @param str the string + * @param start the position + * @returns the byte at that position + */ +unsigned char +_dbus_string_get_byte (const DBusString *str, + int start) +{ + DBUS_CONST_STRING_PREAMBLE (str); + _dbus_assert (start <= real->len); + _dbus_assert (start >= 0); + + return real->str[start]; +} +#endif /* _dbus_string_get_byte */ + +/** + * Inserts a number of bytes of a given value at the + * given position. + * + * @param str the string + * @param i the position + * @param n_bytes number of bytes + * @param byte the value to insert + * @returns #TRUE on success + */ +dbus_bool_t +_dbus_string_insert_bytes (DBusString *str, + int i, + int n_bytes, + unsigned char byte) +{ + DBUS_STRING_PREAMBLE (str); + _dbus_assert (i <= real->len); + _dbus_assert (i >= 0); + _dbus_assert (n_bytes >= 0); + + if (n_bytes == 0) + return TRUE; + + if (!open_gap (n_bytes, real, i)) + return FALSE; + + memset (real->str + i, byte, n_bytes); + + return TRUE; +} + +/** + * Inserts a single byte at the given position. + * + * @param str the string + * @param i the position + * @param byte the value to insert + * @returns #TRUE on success + */ +dbus_bool_t +_dbus_string_insert_byte (DBusString *str, + int i, + unsigned char byte) +{ + DBUS_STRING_PREAMBLE (str); + _dbus_assert (i <= real->len); + _dbus_assert (i >= 0); + + if (!open_gap (1, real, i)) + return FALSE; + + real->str[i] = byte; + + return TRUE; +} + +/** + * Like _dbus_string_get_data(), but removes the + * gotten data from the original string. The caller + * must free the data returned. This function may + * fail due to lack of memory, and return #FALSE. + * + * @param str the string + * @param data_return location to return the buffer + * @returns #TRUE on success + */ +dbus_bool_t +_dbus_string_steal_data (DBusString *str, + char **data_return) +{ + int old_max_length; + DBUS_STRING_PREAMBLE (str); + _dbus_assert (data_return != NULL); + + undo_alignment (real); + + *data_return = (char*) real->str; + + old_max_length = real->max_length; + + /* reset the string */ + if (!_dbus_string_init (str)) + { + /* hrm, put it back then */ + real->str = (unsigned char*) *data_return; + *data_return = NULL; + fixup_alignment (real); + return FALSE; + } + + real->max_length = old_max_length; + + return TRUE; +} + +#ifdef DBUS_BUILD_TESTS +/** + * Like _dbus_string_get_data_len(), but removes the gotten data from + * the original string. The caller must free the data returned. This + * function may fail due to lack of memory, and return #FALSE. + * The returned string is nul-terminated and has length len. + * + * @todo this function is broken because on failure it + * may corrupt the source string. + * + * @param str the string + * @param data_return location to return the buffer + * @param start the start of segment to steal + * @param len the length of segment to steal + * @returns #TRUE on success + */ +dbus_bool_t +_dbus_string_steal_data_len (DBusString *str, + char **data_return, + int start, + int len) +{ + DBusString dest; + DBUS_STRING_PREAMBLE (str); + _dbus_assert (data_return != NULL); + _dbus_assert (start >= 0); + _dbus_assert (len >= 0); + _dbus_assert (start <= real->len); + _dbus_assert (len <= real->len - start); + + if (!_dbus_string_init (&dest)) + return FALSE; + + set_max_length (&dest, real->max_length); + + if (!_dbus_string_move_len (str, start, len, &dest, 0)) + { + _dbus_string_free (&dest); + return FALSE; + } + + _dbus_warn ("Broken code in _dbus_string_steal_data_len(), see @todo, FIXME\n"); + if (!_dbus_string_steal_data (&dest, data_return)) + { + _dbus_string_free (&dest); + return FALSE; + } + + _dbus_string_free (&dest); + return TRUE; +} +#endif /* DBUS_BUILD_TESTS */ + +/** + * Copies the data from the string into a char* + * + * @param str the string + * @param data_return place to return the data + * @returns #TRUE on success, #FALSE on no memory + */ +dbus_bool_t +_dbus_string_copy_data (const DBusString *str, + char **data_return) +{ + DBUS_CONST_STRING_PREAMBLE (str); + _dbus_assert (data_return != NULL); + + *data_return = dbus_malloc (real->len + 1); + if (*data_return == NULL) + return FALSE; + + memcpy (*data_return, real->str, real->len + 1); + + return TRUE; +} + +/** + * Copies the contents of a DBusString into a different buffer. It is + * a bug if avail_len is too short to hold the string contents. nul + * termination is not copied, just the supplied bytes. + * + * @param str a string + * @param buffer a C buffer to copy data to + * @param avail_len maximum length of C buffer + */ +void +_dbus_string_copy_to_buffer (const DBusString *str, + char *buffer, + int avail_len) +{ + DBUS_CONST_STRING_PREAMBLE (str); + + _dbus_assert (avail_len >= 0); + _dbus_assert (avail_len >= real->len); + + memcpy (buffer, real->str, real->len); +} + +/** + * Copies the contents of a DBusString into a different buffer. It is + * a bug if avail_len is too short to hold the string contents plus a + * nul byte. + * + * @param str a string + * @param buffer a C buffer to copy data to + * @param avail_len maximum length of C buffer + */ +void +_dbus_string_copy_to_buffer_with_nul (const DBusString *str, + char *buffer, + int avail_len) +{ + DBUS_CONST_STRING_PREAMBLE (str); + + _dbus_assert (avail_len >= 0); + _dbus_assert (avail_len > real->len); + + memcpy (buffer, real->str, real->len+1); +} + +#ifdef DBUS_BUILD_TESTS +/** + * Copies a segment of the string into a char* + * + * @param str the string + * @param data_return place to return the data + * @param start start index + * @param len length to copy + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_string_copy_data_len (const DBusString *str, + char **data_return, + int start, + int len) +{ + DBusString dest; + + DBUS_CONST_STRING_PREAMBLE (str); + _dbus_assert (data_return != NULL); + _dbus_assert (start >= 0); + _dbus_assert (len >= 0); + _dbus_assert (start <= real->len); + _dbus_assert (len <= real->len - start); + + if (!_dbus_string_init (&dest)) + return FALSE; + + set_max_length (&dest, real->max_length); + + if (!_dbus_string_copy_len (str, start, len, &dest, 0)) + { + _dbus_string_free (&dest); + return FALSE; + } + + if (!_dbus_string_steal_data (&dest, data_return)) + { + _dbus_string_free (&dest); + return FALSE; + } + + _dbus_string_free (&dest); + return TRUE; +} +#endif /* DBUS_BUILD_TESTS */ + +/* Only have the function if we don't have the macro */ +#ifndef _dbus_string_get_length +/** + * Gets the length of a string (not including nul termination). + * + * @returns the length. + */ +int +_dbus_string_get_length (const DBusString *str) +{ + DBUS_CONST_STRING_PREAMBLE (str); + + return real->len; +} +#endif /* !_dbus_string_get_length */ + +/** + * Makes a string longer by the given number of bytes. Checks whether + * adding additional_length to the current length would overflow an + * integer, and checks for exceeding a string's max length. + * The new bytes are not initialized, other than nul-terminating + * the end of the string. The uninitialized bytes may contain + * nul bytes or other junk. + * + * @param str a string + * @param additional_length length to add to the string. + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_string_lengthen (DBusString *str, + int additional_length) +{ + DBUS_STRING_PREAMBLE (str); + _dbus_assert (additional_length >= 0); + + if (_DBUS_UNLIKELY (additional_length > real->max_length - real->len)) + return FALSE; /* would overflow */ + + return set_length (real, + real->len + additional_length); +} + +/** + * Makes a string shorter by the given number of bytes. + * + * @param str a string + * @param length_to_remove length to remove from the string. + */ +void +_dbus_string_shorten (DBusString *str, + int length_to_remove) +{ + DBUS_STRING_PREAMBLE (str); + _dbus_assert (length_to_remove >= 0); + _dbus_assert (length_to_remove <= real->len); + + set_length (real, + real->len - length_to_remove); +} + +/** + * Sets the length of a string. Can be used to truncate or lengthen + * the string. If the string is lengthened, the function may fail and + * return #FALSE. Newly-added bytes are not initialized, as with + * _dbus_string_lengthen(). + * + * @param str a string + * @param length new length of the string. + * @returns #FALSE on failure. + */ +dbus_bool_t +_dbus_string_set_length (DBusString *str, + int length) +{ + DBUS_STRING_PREAMBLE (str); + _dbus_assert (length >= 0); + + return set_length (real, length); +} + +static dbus_bool_t +align_insert_point_then_open_gap (DBusString *str, + int *insert_at_p, + int alignment, + int gap_size) +{ + unsigned long new_len; /* ulong to avoid _DBUS_ALIGN_VALUE overflow */ + unsigned long gap_pos; + int insert_at; + int delta; + DBUS_STRING_PREAMBLE (str); + _dbus_assert (alignment >= 1); + _dbus_assert (alignment <= 8); /* it has to be a bug if > 8 */ + + insert_at = *insert_at_p; + + _dbus_assert (insert_at <= real->len); + + gap_pos = _DBUS_ALIGN_VALUE (insert_at, alignment); + new_len = real->len + (gap_pos - insert_at) + gap_size; + + if (_DBUS_UNLIKELY (new_len > (unsigned long) real->max_length)) + return FALSE; + + delta = new_len - real->len; + _dbus_assert (delta >= 0); + + if (delta == 0) /* only happens if gap_size == 0 and insert_at is aligned already */ + { + _dbus_assert (((unsigned long) *insert_at_p) == gap_pos); + return TRUE; + } + + if (_DBUS_UNLIKELY (!open_gap (new_len - real->len, + real, insert_at))) + return FALSE; + + /* nul the padding if we had to add any padding */ + if (gap_size < delta) + { + memset (&real->str[insert_at], '\0', + gap_pos - insert_at); + } + + *insert_at_p = gap_pos; + + return TRUE; +} + +static dbus_bool_t +align_length_then_lengthen (DBusString *str, + int alignment, + int then_lengthen_by) +{ + int insert_at; + + insert_at = _dbus_string_get_length (str); + + return align_insert_point_then_open_gap (str, + &insert_at, + alignment, then_lengthen_by); +} + +/** + * Align the length of a string to a specific alignment (typically 4 or 8) + * by appending nul bytes to the string. + * + * @param str a string + * @param alignment the alignment + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_string_align_length (DBusString *str, + int alignment) +{ + return align_length_then_lengthen (str, alignment, 0); +} + +/** + * Preallocate extra_bytes such that a future lengthening of the + * string by extra_bytes is guaranteed to succeed without an out of + * memory error. + * + * @param str a string + * @param extra_bytes bytes to alloc + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_string_alloc_space (DBusString *str, + int extra_bytes) +{ + if (!_dbus_string_lengthen (str, extra_bytes)) + return FALSE; + _dbus_string_shorten (str, extra_bytes); + + return TRUE; +} + +static dbus_bool_t +append (DBusRealString *real, + const char *buffer, + int buffer_len) +{ + if (buffer_len == 0) + return TRUE; + + if (!_dbus_string_lengthen ((DBusString*)real, buffer_len)) + return FALSE; + + memcpy (real->str + (real->len - buffer_len), + buffer, + buffer_len); + + return TRUE; +} + +/** + * Appends a nul-terminated C-style string to a DBusString. + * + * @param str the DBusString + * @param buffer the nul-terminated characters to append + * @returns #FALSE if not enough memory. + */ +dbus_bool_t +_dbus_string_append (DBusString *str, + const char *buffer) +{ + unsigned long buffer_len; + + DBUS_STRING_PREAMBLE (str); + _dbus_assert (buffer != NULL); + + buffer_len = strlen (buffer); + if (buffer_len > (unsigned long) real->max_length) + return FALSE; + + return append (real, buffer, buffer_len); +} + +/** assign 2 bytes from one string to another */ +#define ASSIGN_2_OCTETS(p, octets) \ + *((dbus_uint16_t*)(p)) = *((dbus_uint16_t*)(octets)); + +/** assign 4 bytes from one string to another */ +#define ASSIGN_4_OCTETS(p, octets) \ + *((dbus_uint32_t*)(p)) = *((dbus_uint32_t*)(octets)); + +#ifdef DBUS_HAVE_INT64 +/** assign 8 bytes from one string to another */ +#define ASSIGN_8_OCTETS(p, octets) \ + *((dbus_uint64_t*)(p)) = *((dbus_uint64_t*)(octets)); +#else +/** assign 8 bytes from one string to another */ +#define ASSIGN_8_OCTETS(p, octets) \ +do { \ + unsigned char *b; \ + \ + b = p; \ + \ + *b++ = octets[0]; \ + *b++ = octets[1]; \ + *b++ = octets[2]; \ + *b++ = octets[3]; \ + *b++ = octets[4]; \ + *b++ = octets[5]; \ + *b++ = octets[6]; \ + *b++ = octets[7]; \ + _dbus_assert (b == p + 8); \ +} while (0) +#endif /* DBUS_HAVE_INT64 */ + +#ifdef DBUS_BUILD_TESTS +/** + * Appends 4 bytes aligned on a 4 byte boundary + * with any alignment padding initialized to 0. + * + * @param str the DBusString + * @param octets 4 bytes to append + * @returns #FALSE if not enough memory. + */ +dbus_bool_t +_dbus_string_append_4_aligned (DBusString *str, + const unsigned char octets[4]) +{ + DBUS_STRING_PREAMBLE (str); + + if (!align_length_then_lengthen (str, 4, 4)) + return FALSE; + + ASSIGN_4_OCTETS (real->str + (real->len - 4), octets); + + return TRUE; +} +#endif /* DBUS_BUILD_TESTS */ + +#ifdef DBUS_BUILD_TESTS +/** + * Appends 8 bytes aligned on an 8 byte boundary + * with any alignment padding initialized to 0. + * + * @param str the DBusString + * @param octets 8 bytes to append + * @returns #FALSE if not enough memory. + */ +dbus_bool_t +_dbus_string_append_8_aligned (DBusString *str, + const unsigned char octets[8]) +{ + DBUS_STRING_PREAMBLE (str); + + if (!align_length_then_lengthen (str, 8, 8)) + return FALSE; + + ASSIGN_8_OCTETS (real->str + (real->len - 8), octets); + + return TRUE; +} +#endif /* DBUS_BUILD_TESTS */ + +/** + * Inserts 2 bytes aligned on a 2 byte boundary + * with any alignment padding initialized to 0. + * + * @param str the DBusString + * @param insert_at where to insert + * @param octets 2 bytes to insert + * @returns #FALSE if not enough memory. + */ +dbus_bool_t +_dbus_string_insert_2_aligned (DBusString *str, + int insert_at, + const unsigned char octets[4]) +{ + DBUS_STRING_PREAMBLE (str); + + if (!align_insert_point_then_open_gap (str, &insert_at, 2, 2)) + return FALSE; + + ASSIGN_2_OCTETS (real->str + insert_at, octets); + + return TRUE; +} + +/** + * Inserts 4 bytes aligned on a 4 byte boundary + * with any alignment padding initialized to 0. + * + * @param str the DBusString + * @param insert_at where to insert + * @param octets 4 bytes to insert + * @returns #FALSE if not enough memory. + */ +dbus_bool_t +_dbus_string_insert_4_aligned (DBusString *str, + int insert_at, + const unsigned char octets[4]) +{ + DBUS_STRING_PREAMBLE (str); + + if (!align_insert_point_then_open_gap (str, &insert_at, 4, 4)) + return FALSE; + + ASSIGN_4_OCTETS (real->str + insert_at, octets); + + return TRUE; +} + +/** + * Inserts 8 bytes aligned on an 8 byte boundary + * with any alignment padding initialized to 0. + * + * @param str the DBusString + * @param insert_at where to insert + * @param octets 8 bytes to insert + * @returns #FALSE if not enough memory. + */ +dbus_bool_t +_dbus_string_insert_8_aligned (DBusString *str, + int insert_at, + const unsigned char octets[8]) +{ + DBUS_STRING_PREAMBLE (str); + + if (!align_insert_point_then_open_gap (str, &insert_at, 8, 8)) + return FALSE; + + _dbus_assert (_DBUS_ALIGN_VALUE (insert_at, 8) == (unsigned) insert_at); + + ASSIGN_8_OCTETS (real->str + insert_at, octets); + + return TRUE; +} + + +/** + * Inserts padding at *insert_at such to align it to the given + * boundary. Initializes the padding to nul bytes. Sets *insert_at + * to the aligned position. + * + * @param str the DBusString + * @param insert_at location to be aligned + * @param alignment alignment boundary (1, 2, 4, or 8) + * @returns #FALSE if not enough memory. + */ +dbus_bool_t +_dbus_string_insert_alignment (DBusString *str, + int *insert_at, + int alignment) +{ + DBUS_STRING_PREAMBLE (str); + + if (!align_insert_point_then_open_gap (str, insert_at, alignment, 0)) + return FALSE; + + _dbus_assert (_DBUS_ALIGN_VALUE (*insert_at, alignment) == (unsigned) *insert_at); + + return TRUE; +} + +/** + * Appends a printf-style formatted string + * to the #DBusString. + * + * @param str the string + * @param format printf format + * @param args variable argument list + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_string_append_printf_valist (DBusString *str, + const char *format, + va_list args) +{ + int len; + va_list args_copy; + + DBUS_STRING_PREAMBLE (str); + + DBUS_VA_COPY (args_copy, args); + + /* Measure the message length without terminating nul */ + len = _dbus_printf_string_upper_bound (format, args); + + if (!_dbus_string_lengthen (str, len)) + { + /* don't leak the copy */ + va_end (args_copy); + return FALSE; + } + + vsprintf ((char*) (real->str + (real->len - len)), + format, args_copy); + + va_end (args_copy); + + return TRUE; +} + +/** + * Appends a printf-style formatted string + * to the #DBusString. + * + * @param str the string + * @param format printf format + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_string_append_printf (DBusString *str, + const char *format, + ...) +{ + va_list args; + dbus_bool_t retval; + + va_start (args, format); + retval = _dbus_string_append_printf_valist (str, format, args); + va_end (args); + + return retval; +} + +/** + * Appends block of bytes with the given length to a DBusString. + * + * @param str the DBusString + * @param buffer the bytes to append + * @param len the number of bytes to append + * @returns #FALSE if not enough memory. + */ +dbus_bool_t +_dbus_string_append_len (DBusString *str, + const char *buffer, + int len) +{ + DBUS_STRING_PREAMBLE (str); + _dbus_assert (buffer != NULL); + _dbus_assert (len >= 0); + + return append (real, buffer, len); +} + +/** + * Appends a single byte to the string, returning #FALSE + * if not enough memory. + * + * @param str the string + * @param byte the byte to append + * @returns #TRUE on success + */ +dbus_bool_t +_dbus_string_append_byte (DBusString *str, + unsigned char byte) +{ + DBUS_STRING_PREAMBLE (str); + + if (!set_length (real, real->len + 1)) + return FALSE; + + real->str[real->len-1] = byte; + + return TRUE; +} + +#ifdef DBUS_BUILD_TESTS +/** + * Appends a single Unicode character, encoding the character + * in UTF-8 format. + * + * @param str the string + * @param ch the Unicode character + */ +dbus_bool_t +_dbus_string_append_unichar (DBusString *str, + dbus_unichar_t ch) +{ + int len; + int first; + int i; + unsigned char *out; + + DBUS_STRING_PREAMBLE (str); + + /* this code is from GLib but is pretty standard I think */ + + len = 0; + + if (ch < 0x80) + { + first = 0; + len = 1; + } + else if (ch < 0x800) + { + first = 0xc0; + len = 2; + } + else if (ch < 0x10000) + { + first = 0xe0; + len = 3; + } + else if (ch < 0x200000) + { + first = 0xf0; + len = 4; + } + else if (ch < 0x4000000) + { + first = 0xf8; + len = 5; + } + else + { + first = 0xfc; + len = 6; + } + + if (len > (real->max_length - real->len)) + return FALSE; /* real->len + len would overflow */ + + if (!set_length (real, real->len + len)) + return FALSE; + + out = real->str + (real->len - len); + + for (i = len - 1; i > 0; --i) + { + out[i] = (ch & 0x3f) | 0x80; + ch >>= 6; + } + out[0] = ch | first; + + return TRUE; +} +#endif /* DBUS_BUILD_TESTS */ + +static void +delete (DBusRealString *real, + int start, + int len) +{ + if (len == 0) + return; + + memmove (real->str + start, real->str + start + len, real->len - (start + len)); + real->len -= len; + real->str[real->len] = '\0'; +} + +/** + * Deletes a segment of a DBusString with length len starting at + * start. (Hint: to clear an entire string, setting length to 0 + * with _dbus_string_set_length() is easier.) + * + * @param str the DBusString + * @param start where to start deleting + * @param len the number of bytes to delete + */ +void +_dbus_string_delete (DBusString *str, + int start, + int len) +{ + DBUS_STRING_PREAMBLE (str); + _dbus_assert (start >= 0); + _dbus_assert (len >= 0); + _dbus_assert (start <= real->len); + _dbus_assert (len <= real->len - start); + + delete (real, start, len); +} + +static dbus_bool_t +copy (DBusRealString *source, + int start, + int len, + DBusRealString *dest, + int insert_at) +{ + if (len == 0) + return TRUE; + + if (!open_gap (len, dest, insert_at)) + return FALSE; + + memmove (dest->str + insert_at, + source->str + start, + len); + + return TRUE; +} + +/** + * Checks assertions for two strings we're copying a segment between, + * and declares real_source/real_dest variables. + * + * @param source the source string + * @param start the starting offset + * @param dest the dest string + * @param insert_at where the copied segment is inserted + */ +#define DBUS_STRING_COPY_PREAMBLE(source, start, dest, insert_at) \ + DBusRealString *real_source = (DBusRealString*) source; \ + DBusRealString *real_dest = (DBusRealString*) dest; \ + _dbus_assert ((source) != (dest)); \ + DBUS_GENERIC_STRING_PREAMBLE (real_source); \ + DBUS_GENERIC_STRING_PREAMBLE (real_dest); \ + _dbus_assert (!real_dest->constant); \ + _dbus_assert (!real_dest->locked); \ + _dbus_assert ((start) >= 0); \ + _dbus_assert ((start) <= real_source->len); \ + _dbus_assert ((insert_at) >= 0); \ + _dbus_assert ((insert_at) <= real_dest->len) + +/** + * Moves the end of one string into another string. Both strings + * must be initialized, valid strings. + * + * @param source the source string + * @param start where to chop off the source string + * @param dest the destination string + * @param insert_at where to move the chopped-off part of source string + * @returns #FALSE if not enough memory + */ +dbus_bool_t +_dbus_string_move (DBusString *source, + int start, + DBusString *dest, + int insert_at) +{ + DBusRealString *real_source = (DBusRealString*) source; + _dbus_assert (start <= real_source->len); + + return _dbus_string_move_len (source, start, + real_source->len - start, + dest, insert_at); +} + +/** + * Like _dbus_string_move(), but does not delete the section + * of the source string that's copied to the dest string. + * + * @param source the source string + * @param start where to start copying the source string + * @param dest the destination string + * @param insert_at where to place the copied part of source string + * @returns #FALSE if not enough memory + */ +dbus_bool_t +_dbus_string_copy (const DBusString *source, + int start, + DBusString *dest, + int insert_at) +{ + DBUS_STRING_COPY_PREAMBLE (source, start, dest, insert_at); + + return copy (real_source, start, + real_source->len - start, + real_dest, + insert_at); +} + +/** + * Like _dbus_string_move(), but can move a segment from + * the middle of the source string. + * + * @todo this doesn't do anything with max_length field. + * we should probably just kill the max_length field though. + * + * @param source the source string + * @param start first byte of source string to move + * @param len length of segment to move + * @param dest the destination string + * @param insert_at where to move the bytes from the source string + * @returns #FALSE if not enough memory + */ +dbus_bool_t +_dbus_string_move_len (DBusString *source, + int start, + int len, + DBusString *dest, + int insert_at) + +{ + DBUS_STRING_COPY_PREAMBLE (source, start, dest, insert_at); + _dbus_assert (len >= 0); + _dbus_assert ((start + len) <= real_source->len); + + + if (len == 0) + { + return TRUE; + } + else if (start == 0 && + len == real_source->len && + real_dest->len == 0) + { + /* Short-circuit moving an entire existing string to an empty string + * by just swapping the buffers. + */ + /* we assume ->constant doesn't matter as you can't have + * a constant string involved in a move. + */ +#define ASSIGN_DATA(a, b) do { \ + (a)->str = (b)->str; \ + (a)->len = (b)->len; \ + (a)->allocated = (b)->allocated; \ + (a)->align_offset = (b)->align_offset; \ + } while (0) + + DBusRealString tmp; + + ASSIGN_DATA (&tmp, real_source); + ASSIGN_DATA (real_source, real_dest); + ASSIGN_DATA (real_dest, &tmp); + + return TRUE; + } + else + { + if (!copy (real_source, start, len, + real_dest, + insert_at)) + return FALSE; + + delete (real_source, start, + len); + + return TRUE; + } +} + +/** + * Like _dbus_string_copy(), but can copy a segment from the middle of + * the source string. + * + * @param source the source string + * @param start where to start copying the source string + * @param len length of segment to copy + * @param dest the destination string + * @param insert_at where to place the copied segment of source string + * @returns #FALSE if not enough memory + */ +dbus_bool_t +_dbus_string_copy_len (const DBusString *source, + int start, + int len, + DBusString *dest, + int insert_at) +{ + DBUS_STRING_COPY_PREAMBLE (source, start, dest, insert_at); + _dbus_assert (len >= 0); + _dbus_assert (start <= real_source->len); + _dbus_assert (len <= real_source->len - start); + + return copy (real_source, start, len, + real_dest, + insert_at); +} + +/** + * Replaces a segment of dest string with a segment of source string. + * + * @todo optimize the case where the two lengths are the same, and + * avoid memmoving the data in the trailing part of the string twice. + * + * @todo avoid inserting the source into dest, then deleting + * the replaced chunk of dest (which creates a potentially large + * intermediate string). Instead, extend the replaced chunk + * of dest with padding to the same size as the source chunk, + * then copy in the source bytes. + * + * @param source the source string + * @param start where to start copying the source string + * @param len length of segment to copy + * @param dest the destination string + * @param replace_at start of segment of dest string to replace + * @param replace_len length of segment of dest string to replace + * @returns #FALSE if not enough memory + * + */ +dbus_bool_t +_dbus_string_replace_len (const DBusString *source, + int start, + int len, + DBusString *dest, + int replace_at, + int replace_len) +{ + DBUS_STRING_COPY_PREAMBLE (source, start, dest, replace_at); + _dbus_assert (len >= 0); + _dbus_assert (start <= real_source->len); + _dbus_assert (len <= real_source->len - start); + _dbus_assert (replace_at >= 0); + _dbus_assert (replace_at <= real_dest->len); + _dbus_assert (replace_len <= real_dest->len - replace_at); + + if (!copy (real_source, start, len, + real_dest, replace_at)) + return FALSE; + + delete (real_dest, replace_at + len, replace_len); + + return TRUE; +} + +/** + * Looks for the first occurance of a byte, deletes that byte, + * and moves everything after the byte to the beginning of a + * separate string. Both strings must be initialized, valid + * strings. + * + * @param source the source string + * @param byte the byte to remove and split the string at + * @param tail the split off string + * @returns #FALSE if not enough memory or if byte could not be found + * + */ +dbus_bool_t +_dbus_string_split_on_byte (DBusString *source, + unsigned char byte, + DBusString *tail) +{ + int byte_position; + char byte_string[2] = ""; + int head_length; + int tail_length; + + byte_string[0] = (char) byte; + + if (!_dbus_string_find (source, 0, byte_string, &byte_position)) + return FALSE; + + head_length = byte_position; + tail_length = _dbus_string_get_length (source) - head_length - 1; + + if (!_dbus_string_move_len (source, byte_position + 1, tail_length, + tail, 0)) + return FALSE; + + /* remove the trailing delimiter byte from the head now. + */ + if (!_dbus_string_set_length (source, head_length)) + return FALSE; + + return TRUE; +} + +/* Unicode macros and utf8_validate() from GLib Owen Taylor, Havoc + * Pennington, and Tom Tromey are the authors and authorized relicense. + */ + +/** computes length and mask of a unicode character + * @param Char the char + * @param Mask the mask variable to assign to + * @param Len the length variable to assign to + */ +#define UTF8_COMPUTE(Char, Mask, Len) \ + if (Char < 128) \ + { \ + Len = 1; \ + Mask = 0x7f; \ + } \ + else if ((Char & 0xe0) == 0xc0) \ + { \ + Len = 2; \ + Mask = 0x1f; \ + } \ + else if ((Char & 0xf0) == 0xe0) \ + { \ + Len = 3; \ + Mask = 0x0f; \ + } \ + else if ((Char & 0xf8) == 0xf0) \ + { \ + Len = 4; \ + Mask = 0x07; \ + } \ + else if ((Char & 0xfc) == 0xf8) \ + { \ + Len = 5; \ + Mask = 0x03; \ + } \ + else if ((Char & 0xfe) == 0xfc) \ + { \ + Len = 6; \ + Mask = 0x01; \ + } \ + else \ + { \ + Len = 0; \ + Mask = 0; \ + } + +/** + * computes length of a unicode character in UTF-8 + * @param Char the char + */ +#define UTF8_LENGTH(Char) \ + ((Char) < 0x80 ? 1 : \ + ((Char) < 0x800 ? 2 : \ + ((Char) < 0x10000 ? 3 : \ + ((Char) < 0x200000 ? 4 : \ + ((Char) < 0x4000000 ? 5 : 6))))) + +/** + * Gets a UTF-8 value. + * + * @param Result variable for extracted unicode char. + * @param Chars the bytes to decode + * @param Count counter variable + * @param Mask mask for this char + * @param Len length for this char in bytes + */ +#define UTF8_GET(Result, Chars, Count, Mask, Len) \ + (Result) = (Chars)[0] & (Mask); \ + for ((Count) = 1; (Count) < (Len); ++(Count)) \ + { \ + if (((Chars)[(Count)] & 0xc0) != 0x80) \ + { \ + (Result) = -1; \ + break; \ + } \ + (Result) <<= 6; \ + (Result) |= ((Chars)[(Count)] & 0x3f); \ + } + +/** + * Check whether a unicode char is in a valid range. + * + * @param Char the character + */ +#define UNICODE_VALID(Char) \ + ((Char) < 0x110000 && \ + (((Char) & 0xFFFFF800) != 0xD800) && \ + ((Char) < 0xFDD0 || (Char) > 0xFDEF) && \ + ((Char) & 0xFFFF) != 0xFFFF) + +#ifdef DBUS_BUILD_TESTS +/** + * Gets a unicode character from a UTF-8 string. Does no validation; + * you must verify that the string is valid UTF-8 in advance and must + * pass in the start of a character. + * + * @param str the string + * @param start the start of the UTF-8 character. + * @param ch_return location to return the character + * @param end_return location to return the byte index of next character + */ +void +_dbus_string_get_unichar (const DBusString *str, + int start, + dbus_unichar_t *ch_return, + int *end_return) +{ + int i, mask, len; + dbus_unichar_t result; + unsigned char c; + unsigned char *p; + DBUS_CONST_STRING_PREAMBLE (str); + _dbus_assert (start >= 0); + _dbus_assert (start <= real->len); + + if (ch_return) + *ch_return = 0; + if (end_return) + *end_return = real->len; + + mask = 0; + p = real->str + start; + c = *p; + + UTF8_COMPUTE (c, mask, len); + if (len == 0) + return; + UTF8_GET (result, p, i, mask, len); + + if (result == (dbus_unichar_t)-1) + return; + + if (ch_return) + *ch_return = result; + if (end_return) + *end_return = start + len; +} +#endif /* DBUS_BUILD_TESTS */ + +/** + * Finds the given substring in the string, + * returning #TRUE and filling in the byte index + * where the substring was found, if it was found. + * Returns #FALSE if the substring wasn't found. + * Sets *start to the length of the string if the substring + * is not found. + * + * @param str the string + * @param start where to start looking + * @param substr the substring + * @param found return location for where it was found, or #NULL + * @returns #TRUE if found + */ +dbus_bool_t +_dbus_string_find (const DBusString *str, + int start, + const char *substr, + int *found) +{ + return _dbus_string_find_to (str, start, + ((const DBusRealString*)str)->len, + substr, found); +} + +/** + * Finds end of line ("\r\n" or "\n") in the string, + * returning #TRUE and filling in the byte index + * where the eol string was found, if it was found. + * Returns #FALSE if eol wasn't found. + * + * @param str the string + * @param start where to start looking + * @param found return location for where eol was found or string length otherwise + * @param found_len return length of found eol string or zero otherwise + * @returns #TRUE if found + */ +dbus_bool_t +_dbus_string_find_eol (const DBusString *str, + int start, + int *found, + int *found_len) +{ + int i; + + DBUS_CONST_STRING_PREAMBLE (str); + _dbus_assert (start <= real->len); + _dbus_assert (start >= 0); + + i = start; + while (i < real->len) + { + if (real->str[i] == '\r') + { + if ((i+1) < real->len && real->str[i+1] == '\n') /* "\r\n" */ + { + if (found) + *found = i; + if (found_len) + *found_len = 2; + return TRUE; + } + else /* only "\r" */ + { + if (found) + *found = i; + if (found_len) + *found_len = 1; + return TRUE; + } + } + else if (real->str[i] == '\n') /* only "\n" */ + { + if (found) + *found = i; + if (found_len) + *found_len = 1; + return TRUE; + } + ++i; + } + + if (found) + *found = real->len; + + if (found_len) + *found_len = 0; + + return FALSE; +} + +/** + * Finds the given substring in the string, + * up to a certain position, + * returning #TRUE and filling in the byte index + * where the substring was found, if it was found. + * Returns #FALSE if the substring wasn't found. + * Sets *start to the length of the string if the substring + * is not found. + * + * @param str the string + * @param start where to start looking + * @param end where to stop looking + * @param substr the substring + * @param found return location for where it was found, or #NULL + * @returns #TRUE if found + */ +dbus_bool_t +_dbus_string_find_to (const DBusString *str, + int start, + int end, + const char *substr, + int *found) +{ + int i; + DBUS_CONST_STRING_PREAMBLE (str); + _dbus_assert (substr != NULL); + _dbus_assert (start <= real->len); + _dbus_assert (start >= 0); + _dbus_assert (substr != NULL); + _dbus_assert (end <= real->len); + _dbus_assert (start <= end); + + /* we always "find" an empty string */ + if (*substr == '\0') + { + if (found) + *found = start; + return TRUE; + } + + i = start; + while (i < end) + { + if (real->str[i] == substr[0]) + { + int j = i + 1; + + while (j < end) + { + if (substr[j - i] == '\0') + break; + else if (real->str[j] != substr[j - i]) + break; + + ++j; + } + + if (substr[j - i] == '\0') + { + if (found) + *found = i; + return TRUE; + } + } + + ++i; + } + + if (found) + *found = end; + + return FALSE; +} + +/** + * Finds a blank (space or tab) in the string. Returns #TRUE + * if found, #FALSE otherwise. If a blank is not found sets + * *found to the length of the string. + * + * @param str the string + * @param start byte index to start looking + * @param found place to store the location of the first blank + * @returns #TRUE if a blank was found + */ +dbus_bool_t +_dbus_string_find_blank (const DBusString *str, + int start, + int *found) +{ + int i; + DBUS_CONST_STRING_PREAMBLE (str); + _dbus_assert (start <= real->len); + _dbus_assert (start >= 0); + + i = start; + while (i < real->len) + { + if (real->str[i] == ' ' || + real->str[i] == '\t') + { + if (found) + *found = i; + return TRUE; + } + + ++i; + } + + if (found) + *found = real->len; + + return FALSE; +} + +/** + * Skips blanks from start, storing the first non-blank in *end + * (blank is space or tab). + * + * @param str the string + * @param start where to start + * @param end where to store the first non-blank byte index + */ +void +_dbus_string_skip_blank (const DBusString *str, + int start, + int *end) +{ + int i; + DBUS_CONST_STRING_PREAMBLE (str); + _dbus_assert (start <= real->len); + _dbus_assert (start >= 0); + + i = start; + while (i < real->len) + { + if (!DBUS_IS_ASCII_BLANK (real->str[i])) + break; + + ++i; + } + + _dbus_assert (i == real->len || !DBUS_IS_ASCII_WHITE (real->str[i])); + + if (end) + *end = i; +} + + +/** + * Skips whitespace from start, storing the first non-whitespace in *end. + * (whitespace is space, tab, newline, CR). + * + * @param str the string + * @param start where to start + * @param end where to store the first non-whitespace byte index + */ +void +_dbus_string_skip_white (const DBusString *str, + int start, + int *end) +{ + int i; + DBUS_CONST_STRING_PREAMBLE (str); + _dbus_assert (start <= real->len); + _dbus_assert (start >= 0); + + i = start; + while (i < real->len) + { + if (!DBUS_IS_ASCII_WHITE (real->str[i])) + break; + + ++i; + } + + _dbus_assert (i == real->len || !(DBUS_IS_ASCII_WHITE (real->str[i]))); + + if (end) + *end = i; +} + +/** + * Skips whitespace from end, storing the start index of the trailing + * whitespace in *start. (whitespace is space, tab, newline, CR). + * + * @param str the string + * @param end where to start scanning backward + * @param start where to store the start of whitespace chars + */ +void +_dbus_string_skip_white_reverse (const DBusString *str, + int end, + int *start) +{ + int i; + DBUS_CONST_STRING_PREAMBLE (str); + _dbus_assert (end <= real->len); + _dbus_assert (end >= 0); + + i = end; + while (i > 0) + { + if (!DBUS_IS_ASCII_WHITE (real->str[i-1])) + break; + --i; + } + + _dbus_assert (i >= 0 && (i == 0 || !(DBUS_IS_ASCII_WHITE (real->str[i-1])))); + + if (start) + *start = i; +} + +/** + * Assigns a newline-terminated or \\r\\n-terminated line from the front + * of the string to the given dest string. The dest string's previous + * contents are deleted. If the source string contains no newline, + * moves the entire source string to the dest string. + * + * @todo owen correctly notes that this is a stupid function (it was + * written purely for test code, + * e.g. dbus-message-builder.c). Probably should be enforced as test + * code only with ifdef DBUS_BUILD_TESTS + * + * @param source the source string + * @param dest the destination string (contents are replaced) + * @returns #FALSE if no memory, or source has length 0 + */ +dbus_bool_t +_dbus_string_pop_line (DBusString *source, + DBusString *dest) +{ + int eol, eol_len; + + _dbus_string_set_length (dest, 0); + + eol = 0; + eol_len = 0; + if (!_dbus_string_find_eol (source, 0, &eol, &eol_len)) + { + _dbus_assert (eol == _dbus_string_get_length (source)); + if (eol == 0) + { + /* If there's no newline and source has zero length, we're done */ + return FALSE; + } + /* otherwise, the last line of the file has no eol characters */ + } + + /* remember eol can be 0 if it's an empty line, but eol_len should not be zero also + * since find_eol returned TRUE + */ + + if (!_dbus_string_move_len (source, 0, eol + eol_len, dest, 0)) + return FALSE; + + /* remove line ending */ + if (!_dbus_string_set_length (dest, eol)) + { + _dbus_assert_not_reached ("out of memory when shortening a string"); + return FALSE; + } + + return TRUE; +} + +#ifdef DBUS_BUILD_TESTS +/** + * Deletes up to and including the first blank space + * in the string. + * + * @param str the string + */ +void +_dbus_string_delete_first_word (DBusString *str) +{ + int i; + + if (_dbus_string_find_blank (str, 0, &i)) + _dbus_string_skip_blank (str, i, &i); + + _dbus_string_delete (str, 0, i); +} +#endif + +#ifdef DBUS_BUILD_TESTS +/** + * Deletes any leading blanks in the string + * + * @param str the string + */ +void +_dbus_string_delete_leading_blanks (DBusString *str) +{ + int i; + + _dbus_string_skip_blank (str, 0, &i); + + if (i > 0) + _dbus_string_delete (str, 0, i); +} +#endif + +/** + * Deletes leading and trailing whitespace + * + * @param str the string + */ +void +_dbus_string_chop_white(DBusString *str) +{ + int i; + + _dbus_string_skip_white (str, 0, &i); + + if (i > 0) + _dbus_string_delete (str, 0, i); + + _dbus_string_skip_white_reverse (str, _dbus_string_get_length (str), &i); + + _dbus_string_set_length (str, i); +} + +/** + * Tests two DBusString for equality. + * + * @todo memcmp is probably faster + * + * @param a first string + * @param b second string + * @returns #TRUE if equal + */ +dbus_bool_t +_dbus_string_equal (const DBusString *a, + const DBusString *b) +{ + const unsigned char *ap; + const unsigned char *bp; + const unsigned char *a_end; + const DBusRealString *real_a = (const DBusRealString*) a; + const DBusRealString *real_b = (const DBusRealString*) b; + DBUS_GENERIC_STRING_PREAMBLE (real_a); + DBUS_GENERIC_STRING_PREAMBLE (real_b); + + if (real_a->len != real_b->len) + return FALSE; + + ap = real_a->str; + bp = real_b->str; + a_end = real_a->str + real_a->len; + while (ap != a_end) + { + if (*ap != *bp) + return FALSE; + + ++ap; + ++bp; + } + + return TRUE; +} + +#ifdef DBUS_BUILD_TESTS +/** + * Tests two DBusString for equality up to the given length. + * The strings may be shorter than the given length. + * + * @todo write a unit test + * + * @todo memcmp is probably faster + * + * @param a first string + * @param b second string + * @param len the maximum length to look at + * @returns #TRUE if equal for the given number of bytes + */ +dbus_bool_t +_dbus_string_equal_len (const DBusString *a, + const DBusString *b, + int len) +{ + const unsigned char *ap; + const unsigned char *bp; + const unsigned char *a_end; + const DBusRealString *real_a = (const DBusRealString*) a; + const DBusRealString *real_b = (const DBusRealString*) b; + DBUS_GENERIC_STRING_PREAMBLE (real_a); + DBUS_GENERIC_STRING_PREAMBLE (real_b); + + if (real_a->len != real_b->len && + (real_a->len < len || real_b->len < len)) + return FALSE; + + ap = real_a->str; + bp = real_b->str; + a_end = real_a->str + MIN (real_a->len, len); + while (ap != a_end) + { + if (*ap != *bp) + return FALSE; + + ++ap; + ++bp; + } + + return TRUE; +} +#endif /* DBUS_BUILD_TESTS */ + +/** + * Tests two sub-parts of two DBusString for equality. The specified + * range of the first string must exist; the specified start position + * of the second string must exist. + * + * @todo write a unit test + * + * @todo memcmp is probably faster + * + * @param a first string + * @param a_start where to start substring in first string + * @param a_len length of substring in first string + * @param b second string + * @param b_start where to start substring in second string + * @returns #TRUE if the two substrings are equal + */ +dbus_bool_t +_dbus_string_equal_substring (const DBusString *a, + int a_start, + int a_len, + const DBusString *b, + int b_start) +{ + const unsigned char *ap; + const unsigned char *bp; + const unsigned char *a_end; + const DBusRealString *real_a = (const DBusRealString*) a; + const DBusRealString *real_b = (const DBusRealString*) b; + DBUS_GENERIC_STRING_PREAMBLE (real_a); + DBUS_GENERIC_STRING_PREAMBLE (real_b); + _dbus_assert (a_start >= 0); + _dbus_assert (a_len >= 0); + _dbus_assert (a_start <= real_a->len); + _dbus_assert (a_len <= real_a->len - a_start); + _dbus_assert (b_start >= 0); + _dbus_assert (b_start <= real_b->len); + + if (a_len > real_b->len - b_start) + return FALSE; + + ap = real_a->str + a_start; + bp = real_b->str + b_start; + a_end = ap + a_len; + while (ap != a_end) + { + if (*ap != *bp) + return FALSE; + + ++ap; + ++bp; + } + + _dbus_assert (bp <= (real_b->str + real_b->len)); + + return TRUE; +} + +/** + * Checks whether a string is equal to a C string. + * + * @param a the string + * @param c_str the C string + * @returns #TRUE if equal + */ +dbus_bool_t +_dbus_string_equal_c_str (const DBusString *a, + const char *c_str) +{ + const unsigned char *ap; + const unsigned char *bp; + const unsigned char *a_end; + const DBusRealString *real_a = (const DBusRealString*) a; + DBUS_GENERIC_STRING_PREAMBLE (real_a); + _dbus_assert (c_str != NULL); + + ap = real_a->str; + bp = (const unsigned char*) c_str; + a_end = real_a->str + real_a->len; + while (ap != a_end && *bp) + { + if (*ap != *bp) + return FALSE; + + ++ap; + ++bp; + } + + if (ap != a_end || *bp) + return FALSE; + + return TRUE; +} + +#ifdef DBUS_BUILD_TESTS +/** + * Checks whether a string starts with the given C string. + * + * @param a the string + * @param c_str the C string + * @returns #TRUE if string starts with it + */ +dbus_bool_t +_dbus_string_starts_with_c_str (const DBusString *a, + const char *c_str) +{ + const unsigned char *ap; + const unsigned char *bp; + const unsigned char *a_end; + const DBusRealString *real_a = (const DBusRealString*) a; + DBUS_GENERIC_STRING_PREAMBLE (real_a); + _dbus_assert (c_str != NULL); + + ap = real_a->str; + bp = (const unsigned char*) c_str; + a_end = real_a->str + real_a->len; + while (ap != a_end && *bp) + { + if (*ap != *bp) + return FALSE; + + ++ap; + ++bp; + } + + if (*bp == '\0') + return TRUE; + else + return FALSE; +} +#endif /* DBUS_BUILD_TESTS */ + +/** + * Appends a two-character hex digit to a string, where the hex digit + * has the value of the given byte. + * + * @param str the string + * @param byte the byte + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_string_append_byte_as_hex (DBusString *str, + int byte) +{ + const char hexdigits[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'a', 'b', 'c', 'd', 'e', 'f' + }; + + if (!_dbus_string_append_byte (str, + hexdigits[(byte >> 4)])) + return FALSE; + + if (!_dbus_string_append_byte (str, + hexdigits[(byte & 0x0f)])) + { + _dbus_string_set_length (str, + _dbus_string_get_length (str) - 1); + return FALSE; + } + + return TRUE; +} + +/** + * Encodes a string in hex, the way MD5 and SHA-1 are usually + * encoded. (Each byte is two hex digits.) + * + * @param source the string to encode + * @param start byte index to start encoding + * @param dest string where encoded data should be placed + * @param insert_at where to place encoded data + * @returns #TRUE if encoding was successful, #FALSE if no memory etc. + */ +dbus_bool_t +_dbus_string_hex_encode (const DBusString *source, + int start, + DBusString *dest, + int insert_at) +{ + DBusString result; + const unsigned char *p; + const unsigned char *end; + dbus_bool_t retval; + + _dbus_assert (start <= _dbus_string_get_length (source)); + + if (!_dbus_string_init (&result)) + return FALSE; + + retval = FALSE; + + p = (const unsigned char*) _dbus_string_get_const_data (source); + end = p + _dbus_string_get_length (source); + p += start; + + while (p != end) + { + if (!_dbus_string_append_byte_as_hex (&result, *p)) + goto out; + + ++p; + } + + if (!_dbus_string_move (&result, 0, dest, insert_at)) + goto out; + + retval = TRUE; + + out: + _dbus_string_free (&result); + return retval; +} + +/** + * Decodes a string from hex encoding. + * + * @param source the string to decode + * @param start byte index to start decode + * @param end_return return location of the end of the hex data, or #NULL + * @param dest string where decoded data should be placed + * @param insert_at where to place decoded data + * @returns #TRUE if decoding was successful, #FALSE if no memory. + */ +dbus_bool_t +_dbus_string_hex_decode (const DBusString *source, + int start, + int *end_return, + DBusString *dest, + int insert_at) +{ + DBusString result; + const unsigned char *p; + const unsigned char *end; + dbus_bool_t retval; + dbus_bool_t high_bits; + + _dbus_assert (start <= _dbus_string_get_length (source)); + + if (!_dbus_string_init (&result)) + return FALSE; + + retval = FALSE; + + high_bits = TRUE; + p = (const unsigned char*) _dbus_string_get_const_data (source); + end = p + _dbus_string_get_length (source); + p += start; + + while (p != end) + { + unsigned int val; + + switch (*p) + { + case '0': + val = 0; + break; + case '1': + val = 1; + break; + case '2': + val = 2; + break; + case '3': + val = 3; + break; + case '4': + val = 4; + break; + case '5': + val = 5; + break; + case '6': + val = 6; + break; + case '7': + val = 7; + break; + case '8': + val = 8; + break; + case '9': + val = 9; + break; + case 'a': + case 'A': + val = 10; + break; + case 'b': + case 'B': + val = 11; + break; + case 'c': + case 'C': + val = 12; + break; + case 'd': + case 'D': + val = 13; + break; + case 'e': + case 'E': + val = 14; + break; + case 'f': + case 'F': + val = 15; + break; + default: + goto done; + } + + if (high_bits) + { + if (!_dbus_string_append_byte (&result, + val << 4)) + goto out; + } + else + { + int len; + unsigned char b; + + len = _dbus_string_get_length (&result); + + b = _dbus_string_get_byte (&result, len - 1); + + b |= val; + + _dbus_string_set_byte (&result, len - 1, b); + } + + high_bits = !high_bits; + + ++p; + } + + done: + if (!_dbus_string_move (&result, 0, dest, insert_at)) + goto out; + + if (end_return) + *end_return = p - (const unsigned char*) _dbus_string_get_const_data (source); + + retval = TRUE; + + out: + _dbus_string_free (&result); + return retval; +} + +/** + * Checks that the given range of the string is valid ASCII with no + * nul bytes. If the given range is not entirely contained in the + * string, returns #FALSE. + * + * @todo this is inconsistent with most of DBusString in that + * it allows a start,len range that extends past the string end. + * + * @param str the string + * @param start first byte index to check + * @param len number of bytes to check + * @returns #TRUE if the byte range exists and is all valid ASCII + */ +dbus_bool_t +_dbus_string_validate_ascii (const DBusString *str, + int start, + int len) +{ + const unsigned char *s; + const unsigned char *end; + DBUS_CONST_STRING_PREAMBLE (str); + _dbus_assert (start >= 0); + _dbus_assert (start <= real->len); + _dbus_assert (len >= 0); + + if (len > real->len - start) + return FALSE; + + s = real->str + start; + end = s + len; + while (s != end) + { + if (_DBUS_UNLIKELY (!_DBUS_ISASCII (*s))) + return FALSE; + + ++s; + } + + return TRUE; +} + +/** + * Checks that the given range of the string is valid UTF-8. If the + * given range is not entirely contained in the string, returns + * #FALSE. If the string contains any nul bytes in the given range, + * returns #FALSE. If the start and start+len are not on character + * boundaries, returns #FALSE. + * + * @todo this is inconsistent with most of DBusString in that + * it allows a start,len range that extends past the string end. + * + * @param str the string + * @param start first byte index to check + * @param len number of bytes to check + * @returns #TRUE if the byte range exists and is all valid UTF-8 + */ +dbus_bool_t +_dbus_string_validate_utf8 (const DBusString *str, + int start, + int len) +{ + const unsigned char *p; + const unsigned char *end; + DBUS_CONST_STRING_PREAMBLE (str); + _dbus_assert (start >= 0); + _dbus_assert (start <= real->len); + _dbus_assert (len >= 0); + + /* we are doing _DBUS_UNLIKELY() here which might be + * dubious in a generic library like GLib, but in D-Bus + * we know we're validating messages and that it would + * only be evil/broken apps that would have invalid + * UTF-8. Also, this function seems to be a performance + * bottleneck in profiles. + */ + + if (_DBUS_UNLIKELY (len > real->len - start)) + return FALSE; + + p = real->str + start; + end = p + len; + + while (p < end) + { + int i, mask, char_len; + dbus_unichar_t result; + + /* nul bytes considered invalid */ + if (*p == '\0') + break; + + /* Special-case ASCII; this makes us go a lot faster in + * D-Bus profiles where we are typically validating + * function names and such. We have to know that + * all following checks will pass for ASCII though, + * comments follow ... + */ + if (*p < 128) + { + ++p; + continue; + } + + UTF8_COMPUTE (*p, mask, char_len); + + if (_DBUS_UNLIKELY (char_len == 0)) /* ASCII: char_len == 1 */ + break; + + /* check that the expected number of bytes exists in the remaining length */ + if (_DBUS_UNLIKELY ((end - p) < char_len)) /* ASCII: p < end and char_len == 1 */ + break; + + UTF8_GET (result, p, i, mask, char_len); + + /* Check for overlong UTF-8 */ + if (_DBUS_UNLIKELY (UTF8_LENGTH (result) != char_len)) /* ASCII: UTF8_LENGTH == 1 */ + break; +#if 0 + /* The UNICODE_VALID check below will catch this */ + if (_DBUS_UNLIKELY (result == (dbus_unichar_t)-1)) /* ASCII: result = ascii value */ + break; +#endif + + if (_DBUS_UNLIKELY (!UNICODE_VALID (result))) /* ASCII: always valid */ + break; + + /* UNICODE_VALID should have caught it */ + _dbus_assert (result != (dbus_unichar_t)-1); + + p += char_len; + } + + /* See that we covered the entire length if a length was + * passed in + */ + if (_DBUS_UNLIKELY (p != end)) + return FALSE; + else + return TRUE; +} + +/** + * Checks that the given range of the string is all nul bytes. If the + * given range is not entirely contained in the string, returns + * #FALSE. + * + * @todo this is inconsistent with most of DBusString in that + * it allows a start,len range that extends past the string end. + * + * @param str the string + * @param start first byte index to check + * @param len number of bytes to check + * @returns #TRUE if the byte range exists and is all nul bytes + */ +dbus_bool_t +_dbus_string_validate_nul (const DBusString *str, + int start, + int len) +{ + const unsigned char *s; + const unsigned char *end; + DBUS_CONST_STRING_PREAMBLE (str); + _dbus_assert (start >= 0); + _dbus_assert (len >= 0); + _dbus_assert (start <= real->len); + + if (len > real->len - start) + return FALSE; + + s = real->str + start; + end = s + len; + while (s != end) + { + if (_DBUS_UNLIKELY (*s != '\0')) + return FALSE; + ++s; + } + + return TRUE; +} + +/** + * Clears all allocated bytes in the string to zero. + * + * @param str the string + */ +void +_dbus_string_zero (DBusString *str) +{ + DBUS_STRING_PREAMBLE (str); + + memset (real->str - real->align_offset, '\0', real->allocated); +} +/** @} */ + +/* tests are in dbus-string-util.c */ diff --git a/src/dbus/dbus-string.h b/src/dbus/dbus-string.h new file mode 100644 index 0000000..374f0a8 --- /dev/null +++ b/src/dbus/dbus-string.h @@ -0,0 +1,321 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-string.h String utility class (internal to D-Bus implementation) + * + * Copyright (C) 2002, 2003 Red Hat, Inc. + * Copyright (C) 2006 Ralf Habacker + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef DBUS_STRING_H +#define DBUS_STRING_H + +#include + +#include +#include +#include + +#include + +DBUS_BEGIN_DECLS + +/** + * DBusString object + */ +struct DBusString +{ + const void *dummy1; /**< placeholder */ + int dummy2; /**< placeholder */ + int dummy3; /**< placeholder */ + int dummy4; /**< placeholder */ + unsigned int dummy5 : 1; /**< placeholder */ + unsigned int dummy6 : 1; /**< placeholder */ + unsigned int dummy7 : 1; /**< placeholder */ + unsigned int dummy8 : 3; /**< placeholder */ +}; + +#ifdef DBUS_DISABLE_ASSERT +/* Some simple inlining hacks; the current linker is not smart enough + * to inline non-exported symbols across files in the library. + * Note that these break type safety (due to the casts) + */ +#define _dbus_string_get_data(s) ((char*)(((DBusString*)(s))->dummy1)) +#define _dbus_string_get_length(s) (((DBusString*)(s))->dummy2) +#define _dbus_string_set_byte(s, i, b) ((((unsigned char*)(((DBusString*)(s))->dummy1))[(i)]) = (unsigned char) (b)) +#define _dbus_string_get_byte(s, i) (((const unsigned char*)(((DBusString*)(s))->dummy1))[(i)]) +#define _dbus_string_get_const_data(s) ((const char*)(((DBusString*)(s))->dummy1)) +#define _dbus_string_get_const_data_len(s,start,len) (((const char*)(((DBusString*)(s))->dummy1)) + (start)) +#endif + +dbus_bool_t _dbus_string_init (DBusString *str); +void _dbus_string_init_const (DBusString *str, + const char *value); +void _dbus_string_init_const_len (DBusString *str, + const char *value, + int len); +dbus_bool_t _dbus_string_init_preallocated (DBusString *str, + int allocate_size); +void _dbus_string_free (DBusString *str); +void _dbus_string_lock (DBusString *str); +dbus_bool_t _dbus_string_compact (DBusString *str, + int max_waste); +#ifndef _dbus_string_get_data +char* _dbus_string_get_data (DBusString *str); +#endif /* _dbus_string_get_data */ +#ifndef _dbus_string_get_const_data +const char* _dbus_string_get_const_data (const DBusString *str); +#endif /* _dbus_string_get_const_data */ +char* _dbus_string_get_data_len (DBusString *str, + int start, + int len); +#ifndef _dbus_string_get_const_data_len +const char* _dbus_string_get_const_data_len (const DBusString *str, + int start, + int len); +#endif +#ifndef _dbus_string_set_byte +void _dbus_string_set_byte (DBusString *str, + int i, + unsigned char byte); +#endif +#ifndef _dbus_string_get_byte +unsigned char _dbus_string_get_byte (const DBusString *str, + int start); +#endif /* _dbus_string_get_byte */ +dbus_bool_t _dbus_string_insert_bytes (DBusString *str, + int i, + int n_bytes, + unsigned char byte); +dbus_bool_t _dbus_string_insert_byte (DBusString *str, + int i, + unsigned char byte); +dbus_bool_t _dbus_string_steal_data (DBusString *str, + char **data_return); +dbus_bool_t _dbus_string_steal_data_len (DBusString *str, + char **data_return, + int start, + int len); +dbus_bool_t _dbus_string_copy_data (const DBusString *str, + char **data_return); +dbus_bool_t _dbus_string_copy_data_len (const DBusString *str, + char **data_return, + int start, + int len); +void _dbus_string_copy_to_buffer (const DBusString *str, + char *buffer, + int len); +void _dbus_string_copy_to_buffer_with_nul (const DBusString *str, + char *buffer, + int avail_len); +#ifndef _dbus_string_get_length +int _dbus_string_get_length (const DBusString *str); +#endif /* !_dbus_string_get_length */ + +dbus_bool_t _dbus_string_lengthen (DBusString *str, + int additional_length); +void _dbus_string_shorten (DBusString *str, + int length_to_remove); +dbus_bool_t _dbus_string_set_length (DBusString *str, + int length); +dbus_bool_t _dbus_string_align_length (DBusString *str, + int alignment); +dbus_bool_t _dbus_string_alloc_space (DBusString *str, + int extra_bytes); +dbus_bool_t _dbus_string_append (DBusString *str, + const char *buffer); +dbus_bool_t _dbus_string_append_len (DBusString *str, + const char *buffer, + int len); +dbus_bool_t _dbus_string_append_int (DBusString *str, + long value); +dbus_bool_t _dbus_string_append_uint (DBusString *str, + unsigned long value); +dbus_bool_t _dbus_string_append_double (DBusString *str, + double value); +dbus_bool_t _dbus_string_append_byte (DBusString *str, + unsigned char byte); +dbus_bool_t _dbus_string_append_unichar (DBusString *str, + dbus_unichar_t ch); +dbus_bool_t _dbus_string_append_4_aligned (DBusString *str, + const unsigned char octets[4]); +dbus_bool_t _dbus_string_append_8_aligned (DBusString *str, + const unsigned char octets[8]); +dbus_bool_t _dbus_string_append_printf (DBusString *str, + const char *format, + ...) _DBUS_GNUC_PRINTF (2, 3); +dbus_bool_t _dbus_string_append_printf_valist (DBusString *str, + const char *format, + va_list args); +dbus_bool_t _dbus_string_insert_2_aligned (DBusString *str, + int insert_at, + const unsigned char octets[2]); +dbus_bool_t _dbus_string_insert_4_aligned (DBusString *str, + int insert_at, + const unsigned char octets[4]); +dbus_bool_t _dbus_string_insert_8_aligned (DBusString *str, + int insert_at, + const unsigned char octets[8]); +dbus_bool_t _dbus_string_insert_alignment (DBusString *str, + int *insert_at, + int alignment); +void _dbus_string_delete (DBusString *str, + int start, + int len); +dbus_bool_t _dbus_string_move (DBusString *source, + int start, + DBusString *dest, + int insert_at); +dbus_bool_t _dbus_string_copy (const DBusString *source, + int start, + DBusString *dest, + int insert_at); +dbus_bool_t _dbus_string_move_len (DBusString *source, + int start, + int len, + DBusString *dest, + int insert_at); +dbus_bool_t _dbus_string_copy_len (const DBusString *source, + int start, + int len, + DBusString *dest, + int insert_at); +dbus_bool_t _dbus_string_replace_len (const DBusString *source, + int start, + int len, + DBusString *dest, + int replace_at, + int replace_len); +dbus_bool_t _dbus_string_split_on_byte (DBusString *source, + unsigned char byte, + DBusString *tail); +void _dbus_string_get_unichar (const DBusString *str, + int start, + dbus_unichar_t *ch_return, + int *end_return); +dbus_bool_t _dbus_string_parse_int (const DBusString *str, + int start, + long *value_return, + int *end_return); +dbus_bool_t _dbus_string_parse_uint (const DBusString *str, + int start, + unsigned long *value_return, + int *end_return); +dbus_bool_t _dbus_string_parse_double (const DBusString *str, + int start, + double *value, + int *end_return); +dbus_bool_t _dbus_string_find (const DBusString *str, + int start, + const char *substr, + int *found); +dbus_bool_t _dbus_string_find_eol (const DBusString *str, + int start, + int *found, + int *found_len); +dbus_bool_t _dbus_string_find_to (const DBusString *str, + int start, + int end, + const char *substr, + int *found); +dbus_bool_t _dbus_string_find_byte_backward (const DBusString *str, + int start, + unsigned char byte, + int *found); +dbus_bool_t _dbus_string_find_blank (const DBusString *str, + int start, + int *found); +void _dbus_string_skip_blank (const DBusString *str, + int start, + int *end); +void _dbus_string_skip_white (const DBusString *str, + int start, + int *end); +void _dbus_string_skip_white_reverse (const DBusString *str, + int end, + int *start); +dbus_bool_t _dbus_string_equal (const DBusString *a, + const DBusString *b); +dbus_bool_t _dbus_string_equal_c_str (const DBusString *a, + const char *c_str); +dbus_bool_t _dbus_string_equal_len (const DBusString *a, + const DBusString *b, + int len); +dbus_bool_t _dbus_string_equal_substring (const DBusString *a, + int a_start, + int a_len, + const DBusString *b, + int b_start); +dbus_bool_t _dbus_string_starts_with_c_str (const DBusString *a, + const char *c_str); +dbus_bool_t _dbus_string_ends_with_c_str (const DBusString *a, + const char *c_str); +dbus_bool_t _dbus_string_pop_line (DBusString *source, + DBusString *dest); +void _dbus_string_delete_first_word (DBusString *str); +void _dbus_string_delete_leading_blanks (DBusString *str); +void _dbus_string_chop_white (DBusString *str); +dbus_bool_t _dbus_string_append_byte_as_hex (DBusString *str, + int byte); +dbus_bool_t _dbus_string_hex_encode (const DBusString *source, + int start, + DBusString *dest, + int insert_at); +dbus_bool_t _dbus_string_hex_decode (const DBusString *source, + int start, + int *end_return, + DBusString *dest, + int insert_at); +dbus_bool_t _dbus_string_validate_ascii (const DBusString *str, + int start, + int len); +dbus_bool_t _dbus_string_validate_utf8 (const DBusString *str, + int start, + int len); +dbus_bool_t _dbus_string_validate_nul (const DBusString *str, + int start, + int len); +void _dbus_string_zero (DBusString *str); + + +/** + * We allocate 1 byte for nul termination, plus 7 bytes for possible + * align_offset, so we always need 8 bytes on top of the string's + * length to be in the allocated block. + */ +#define _DBUS_STRING_ALLOCATION_PADDING 8 + +/** + * Defines a static const variable with type #DBusString called "name" + * containing the given string literal. + * + * @param name the name of the variable + * @param str the string value + */ +#define _DBUS_STRING_DEFINE_STATIC(name, str) \ + static const char _dbus_static_string_##name[] = str; \ + static const DBusString name = { _dbus_static_string_##name, \ + sizeof(_dbus_static_string_##name), \ + sizeof(_dbus_static_string_##name) + \ + _DBUS_STRING_ALLOCATION_PADDING, \ + sizeof(_dbus_static_string_##name), \ + TRUE, TRUE, FALSE, 0 } + +DBUS_END_DECLS + +#endif /* DBUS_STRING_H */ diff --git a/src/dbus/dbus-sysdeps-pthread.c b/src/dbus/dbus-sysdeps-pthread.c new file mode 100644 index 0000000..121ee12 --- /dev/null +++ b/src/dbus/dbus-sysdeps-pthread.c @@ -0,0 +1,324 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-sysdeps-pthread.c Implements threads using pthreads (internal to libdbus) + * + * Copyright (C) 2002, 2003, 2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-internals.h" +#include "dbus-sysdeps.h" +#include "dbus-threads.h" + +#include +#include +#include + +#ifdef HAVE_ERRNO_H +#include +#endif + +typedef struct { + pthread_mutex_t lock; /**< lock protecting count field */ + volatile int count; /**< count of how many times lock holder has recursively locked */ + volatile pthread_t holder; /**< holder of the lock if count >0, + valid but arbitrary thread if count + has ever been >0, uninitialized memory + if count has never been >0 */ +} DBusMutexPThread; + +typedef struct { + pthread_cond_t cond; /**< the condition */ +} DBusCondVarPThread; + +#define DBUS_MUTEX(m) ((DBusMutex*) m) +#define DBUS_MUTEX_PTHREAD(m) ((DBusMutexPThread*) m) + +#define DBUS_COND_VAR(c) ((DBusCondVar*) c) +#define DBUS_COND_VAR_PTHREAD(c) ((DBusCondVarPThread*) c) + + +#ifdef DBUS_DISABLE_ASSERT +/* (tmp != 0) is a no-op usage to silence compiler */ +#define PTHREAD_CHECK(func_name, result_or_call) \ + do { int tmp = (result_or_call); if (tmp != 0) {;} } while (0) +#else +#define PTHREAD_CHECK(func_name, result_or_call) do { \ + int tmp = (result_or_call); \ + if (tmp != 0) { \ + _dbus_warn_check_failed ("pthread function %s failed with %d %s in %s\n", \ + func_name, tmp, strerror(tmp), _DBUS_FUNCTION_NAME); \ + } \ +} while (0) +#endif /* !DBUS_DISABLE_ASSERT */ + +static DBusMutex* +_dbus_pthread_mutex_new (void) +{ + DBusMutexPThread *pmutex; + int result; + + pmutex = dbus_new (DBusMutexPThread, 1); + if (pmutex == NULL) + return NULL; + + result = pthread_mutex_init (&pmutex->lock, NULL); + + if (result == ENOMEM || result == EAGAIN) + { + dbus_free (pmutex); + return NULL; + } + else + { + PTHREAD_CHECK ("pthread_mutex_init", result); + } + + /* Only written */ + pmutex->count = 0; + + /* There's no portable way to have a "null" pthread afaik so we + * can't set pmutex->holder to anything sensible. We only access it + * once the lock is held (which means we've set it). + */ + + return DBUS_MUTEX (pmutex); +} + +static void +_dbus_pthread_mutex_free (DBusMutex *mutex) +{ + DBusMutexPThread *pmutex = DBUS_MUTEX_PTHREAD (mutex); + + _dbus_assert (pmutex->count == 0); + + PTHREAD_CHECK ("pthread_mutex_destroy", pthread_mutex_destroy (&pmutex->lock)); + + dbus_free (pmutex); +} + +static void +_dbus_pthread_mutex_lock (DBusMutex *mutex) +{ + DBusMutexPThread *pmutex = DBUS_MUTEX_PTHREAD (mutex); + pthread_t self = pthread_self (); + + /* If the count is > 0 then someone had the lock, maybe us. If it is + * 0, then it might immediately change right after we read it, + * but it will be changed by another thread; i.e. if we read 0, + * we assume that this thread doesn't have the lock. + * + * Not 100% sure this is safe, but ... seems like it should be. + */ + if (pmutex->count == 0) + { + /* We know we don't have the lock; someone may have the lock. */ + + PTHREAD_CHECK ("pthread_mutex_lock", pthread_mutex_lock (&pmutex->lock)); + + /* We now have the lock. Count must be 0 since it must be 0 when + * the lock is released by another thread, and we just now got + * the lock. + */ + _dbus_assert (pmutex->count == 0); + + pmutex->holder = self; + pmutex->count = 1; + } + else + { + /* We know someone had the lock, possibly us. Thus + * pmutex->holder is not pointing to junk, though it may not be + * the lock holder anymore if the lock holder is not us. If the + * lock holder is us, then we definitely have the lock. + */ + + if (pthread_equal (pmutex->holder, self)) + { + /* We already have the lock. */ + _dbus_assert (pmutex->count > 0); + } + else + { + /* Wait for the lock */ + PTHREAD_CHECK ("pthread_mutex_lock", pthread_mutex_lock (&pmutex->lock)); + pmutex->holder = self; + _dbus_assert (pmutex->count == 0); + } + + pmutex->count += 1; + } +} + +static void +_dbus_pthread_mutex_unlock (DBusMutex *mutex) +{ + DBusMutexPThread *pmutex = DBUS_MUTEX_PTHREAD (mutex); + + _dbus_assert (pmutex->count > 0); + + pmutex->count -= 1; + + if (pmutex->count == 0) + PTHREAD_CHECK ("pthread_mutex_unlock", pthread_mutex_unlock (&pmutex->lock)); + + /* We leave pmutex->holder set to ourselves, its content is undefined if count is 0 */ +} + +static DBusCondVar * +_dbus_pthread_condvar_new (void) +{ + DBusCondVarPThread *pcond; + int result; + + pcond = dbus_new (DBusCondVarPThread, 1); + if (pcond == NULL) + return NULL; + + result = pthread_cond_init (&pcond->cond, NULL); + + if (result == EAGAIN || result == ENOMEM) + { + dbus_free (pcond); + return NULL; + } + else + { + PTHREAD_CHECK ("pthread_cond_init", result); + } + + return DBUS_COND_VAR (pcond); +} + +static void +_dbus_pthread_condvar_free (DBusCondVar *cond) +{ + DBusCondVarPThread *pcond = DBUS_COND_VAR_PTHREAD (cond); + + PTHREAD_CHECK ("pthread_cond_destroy", pthread_cond_destroy (&pcond->cond)); + + dbus_free (pcond); +} + +static void +_dbus_pthread_condvar_wait (DBusCondVar *cond, + DBusMutex *mutex) +{ + DBusMutexPThread *pmutex = DBUS_MUTEX_PTHREAD (mutex); + DBusCondVarPThread *pcond = DBUS_COND_VAR_PTHREAD (cond); + int old_count; + + _dbus_assert (pmutex->count > 0); + _dbus_assert (pthread_equal (pmutex->holder, pthread_self ())); + + old_count = pmutex->count; + pmutex->count = 0; /* allow other threads to lock */ + PTHREAD_CHECK ("pthread_cond_wait", pthread_cond_wait (&pcond->cond, &pmutex->lock)); + _dbus_assert (pmutex->count == 0); + pmutex->count = old_count; + pmutex->holder = pthread_self(); /* other threads may have locked the mutex in the meantime */ +} + +static dbus_bool_t +_dbus_pthread_condvar_wait_timeout (DBusCondVar *cond, + DBusMutex *mutex, + int timeout_milliseconds) +{ + DBusMutexPThread *pmutex = DBUS_MUTEX_PTHREAD (mutex); + DBusCondVarPThread *pcond = DBUS_COND_VAR_PTHREAD (cond); + struct timeval time_now; + struct timespec end_time; + int result; + int old_count; + + _dbus_assert (pmutex->count > 0); + _dbus_assert (pthread_equal (pmutex->holder, pthread_self ())); + + gettimeofday (&time_now, NULL); + + end_time.tv_sec = time_now.tv_sec + timeout_milliseconds / 1000; + end_time.tv_nsec = (time_now.tv_usec + (timeout_milliseconds % 1000) * 1000) * 1000; + if (end_time.tv_nsec > 1000*1000*1000) + { + end_time.tv_sec += 1; + end_time.tv_nsec -= 1000*1000*1000; + } + + old_count = pmutex->count; + pmutex->count = 0; + result = pthread_cond_timedwait (&pcond->cond, &pmutex->lock, &end_time); + + if (result != ETIMEDOUT) + { + PTHREAD_CHECK ("pthread_cond_timedwait", result); + } + + _dbus_assert (pmutex->count == 0); + pmutex->count = old_count; + pmutex->holder = pthread_self(); /* other threads may have locked the mutex in the meantime */ + + /* return true if we did not time out */ + return result != ETIMEDOUT; +} + +static void +_dbus_pthread_condvar_wake_one (DBusCondVar *cond) +{ + DBusCondVarPThread *pcond = DBUS_COND_VAR_PTHREAD (cond); + + PTHREAD_CHECK ("pthread_cond_signal", pthread_cond_signal (&pcond->cond)); +} + +static void +_dbus_pthread_condvar_wake_all (DBusCondVar *cond) +{ + DBusCondVarPThread *pcond = DBUS_COND_VAR_PTHREAD (cond); + + PTHREAD_CHECK ("pthread_cond_broadcast", pthread_cond_broadcast (&pcond->cond)); +} + +static const DBusThreadFunctions pthread_functions = +{ + DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_NEW_MASK | + DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_FREE_MASK | + DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_LOCK_MASK | + DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_UNLOCK_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK| + DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK, + NULL, NULL, NULL, NULL, + _dbus_pthread_condvar_new, + _dbus_pthread_condvar_free, + _dbus_pthread_condvar_wait, + _dbus_pthread_condvar_wait_timeout, + _dbus_pthread_condvar_wake_one, + _dbus_pthread_condvar_wake_all, + _dbus_pthread_mutex_new, + _dbus_pthread_mutex_free, + _dbus_pthread_mutex_lock, + _dbus_pthread_mutex_unlock +}; + +dbus_bool_t +_dbus_threads_init_platform_specific (void) +{ + return dbus_threads_init (&pthread_functions); +} diff --git a/src/dbus/dbus-sysdeps-unix.c b/src/dbus/dbus-sysdeps-unix.c new file mode 100644 index 0000000..ccb8483 --- /dev/null +++ b/src/dbus/dbus-sysdeps-unix.c @@ -0,0 +1,3356 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-sysdeps-unix.c Wrappers around UNIX system/libc features (internal to D-Bus implementation) + * + * Copyright (C) 2002, 2003, 2006 Red Hat, Inc. + * Copyright (C) 2003 CodeFactory AB + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define _GNU_SOURCE + +#include "dbus-internals.h" +#include "dbus-sysdeps.h" +#include "dbus-sysdeps-unix.h" +#include "dbus-threads.h" +#include "dbus-protocol.h" +#include "dbus-transport.h" +#include "dbus-string.h" +#include "dbus-userdb.h" +#include "dbus-list.h" +#include "dbus-credentials.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_WRITEV +#include +#endif +#ifdef HAVE_POLL +#include +#endif +#ifdef HAVE_BACKTRACE +#include +#endif +#ifdef HAVE_GETPEERUCRED +#include +#endif + +#ifdef HAVE_ADT +#include +#endif + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#ifndef AI_ADDRCONFIG +#define AI_ADDRCONFIG 0 +#endif + +#ifndef HAVE_SOCKLEN_T +#define socklen_t int +#endif + +static dbus_bool_t +_dbus_open_socket (int *fd_p, + int domain, + int type, + int protocol, + DBusError *error) +{ + *fd_p = socket (domain, type, protocol); + if (*fd_p >= 0) + { + _dbus_verbose ("socket fd %d opened\n", *fd_p); + return TRUE; + } + else + { + dbus_set_error(error, + _dbus_error_from_errno (errno), + "Failed to open socket: %s", + _dbus_strerror (errno)); + return FALSE; + } +} + +dbus_bool_t +_dbus_open_tcp_socket (int *fd, + DBusError *error) +{ + return _dbus_open_socket(fd, AF_INET, SOCK_STREAM, 0, error); +} + +/** + * Opens a UNIX domain socket (as in the socket() call). + * Does not bind the socket. + * @param fd return location for socket descriptor + * @param error return location for an error + * @returns #FALSE if error is set + */ +dbus_bool_t +_dbus_open_unix_socket (int *fd, + DBusError *error) +{ + return _dbus_open_socket(fd, PF_UNIX, SOCK_STREAM, 0, error); +} + +/** + * Closes a socket. Should not be used on non-socket + * file descriptors or handles. + * + * @param fd the socket + * @param error return location for an error + * @returns #FALSE if error is set + */ +dbus_bool_t +_dbus_close_socket (int fd, + DBusError *error) +{ + return _dbus_close (fd, error); +} + +/** + * Like _dbus_read(), but only works on sockets so is + * available on Windows. + * + * @param fd the socket + * @param buffer string to append data to + * @param count max amount of data to read + * @returns number of bytes appended to the string + */ +int +_dbus_read_socket (int fd, + DBusString *buffer, + int count) +{ + return _dbus_read (fd, buffer, count); +} + +/** + * Like _dbus_write(), but only supports sockets + * and is thus available on Windows. + * + * @param fd the file descriptor to write + * @param buffer the buffer to write data from + * @param start the first byte in the buffer to write + * @param len the number of bytes to try to write + * @returns the number of bytes written or -1 on error + */ +int +_dbus_write_socket (int fd, + const DBusString *buffer, + int start, + int len) +{ + return _dbus_write (fd, buffer, start, len); +} + +/** + * write data to a pipe. + * + * @param pipe the pipe instance + * @param buffer the buffer to write data from + * @param start the first byte in the buffer to write + * @param len the number of bytes to try to write + * @param error error return + * @returns the number of bytes written or -1 on error + */ +int +_dbus_pipe_write (DBusPipe *pipe, + const DBusString *buffer, + int start, + int len, + DBusError *error) +{ + int written; + + written = _dbus_write (pipe->fd_or_handle, buffer, start, len); + if (written < 0) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Writing to pipe: %s\n", + _dbus_strerror (errno)); + } + return written; +} + +/** + * close a pipe. + * + * @param pipe the pipe instance + * @param error return location for an error + * @returns #FALSE if error is set + */ +int +_dbus_pipe_close (DBusPipe *pipe, + DBusError *error) +{ + if (_dbus_close (pipe->fd_or_handle, error) < 0) + { + return -1; + } + else + { + _dbus_pipe_invalidate (pipe); + return 0; + } +} + +/** + * Like _dbus_write_two() but only works on sockets and is thus + * available on Windows. + * + * @param fd the file descriptor + * @param buffer1 first buffer + * @param start1 first byte to write in first buffer + * @param len1 number of bytes to write from first buffer + * @param buffer2 second buffer, or #NULL + * @param start2 first byte to write in second buffer + * @param len2 number of bytes to write in second buffer + * @returns total bytes written from both buffers, or -1 on error + */ +int +_dbus_write_socket_two (int fd, + const DBusString *buffer1, + int start1, + int len1, + const DBusString *buffer2, + int start2, + int len2) +{ + return _dbus_write_two (fd, buffer1, start1, len1, + buffer2, start2, len2); +} + + +/** + * Thin wrapper around the read() system call that appends + * the data it reads to the DBusString buffer. It appends + * up to the given count, and returns the same value + * and same errno as read(). The only exception is that + * _dbus_read() handles EINTR for you. Also, _dbus_read() can + * return ENOMEM, even though regular UNIX read doesn't. + * + * Unlike _dbus_read_socket(), _dbus_read() is not available + * on Windows. + * + * @param fd the file descriptor to read from + * @param buffer the buffer to append data to + * @param count the amount of data to read + * @returns the number of bytes read or -1 + */ +int +_dbus_read (int fd, + DBusString *buffer, + int count) +{ + int bytes_read; + int start; + char *data; + + _dbus_assert (count >= 0); + + start = _dbus_string_get_length (buffer); + + if (!_dbus_string_lengthen (buffer, count)) + { + errno = ENOMEM; + return -1; + } + + data = _dbus_string_get_data_len (buffer, start, count); + + again: + + bytes_read = read (fd, data, count); + + if (bytes_read < 0) + { + if (errno == EINTR) + goto again; + else + { + /* put length back (note that this doesn't actually realloc anything) */ + _dbus_string_set_length (buffer, start); + return -1; + } + } + else + { + /* put length back (doesn't actually realloc) */ + _dbus_string_set_length (buffer, start + bytes_read); + +#if 0 + if (bytes_read > 0) + _dbus_verbose_bytes_of_string (buffer, start, bytes_read); +#endif + + return bytes_read; + } +} + +/** + * Thin wrapper around the write() system call that writes a part of a + * DBusString and handles EINTR for you. + * + * @param fd the file descriptor to write + * @param buffer the buffer to write data from + * @param start the first byte in the buffer to write + * @param len the number of bytes to try to write + * @returns the number of bytes written or -1 on error + */ +int +_dbus_write (int fd, + const DBusString *buffer, + int start, + int len) +{ + const char *data; + int bytes_written; + + data = _dbus_string_get_const_data_len (buffer, start, len); + + again: + + bytes_written = write (fd, data, len); + + if (bytes_written < 0 && errno == EINTR) + goto again; + +#if 0 + if (bytes_written > 0) + _dbus_verbose_bytes_of_string (buffer, start, bytes_written); +#endif + + return bytes_written; +} + +/** + * Like _dbus_write() but will use writev() if possible + * to write both buffers in sequence. The return value + * is the number of bytes written in the first buffer, + * plus the number written in the second. If the first + * buffer is written successfully and an error occurs + * writing the second, the number of bytes in the first + * is returned (i.e. the error is ignored), on systems that + * don't have writev. Handles EINTR for you. + * The second buffer may be #NULL. + * + * @param fd the file descriptor + * @param buffer1 first buffer + * @param start1 first byte to write in first buffer + * @param len1 number of bytes to write from first buffer + * @param buffer2 second buffer, or #NULL + * @param start2 first byte to write in second buffer + * @param len2 number of bytes to write in second buffer + * @returns total bytes written from both buffers, or -1 on error + */ +int +_dbus_write_two (int fd, + const DBusString *buffer1, + int start1, + int len1, + const DBusString *buffer2, + int start2, + int len2) +{ + _dbus_assert (buffer1 != NULL); + _dbus_assert (start1 >= 0); + _dbus_assert (start2 >= 0); + _dbus_assert (len1 >= 0); + _dbus_assert (len2 >= 0); + +#ifdef HAVE_WRITEV + { + struct iovec vectors[2]; + const char *data1; + const char *data2; + int bytes_written; + + data1 = _dbus_string_get_const_data_len (buffer1, start1, len1); + + if (buffer2 != NULL) + data2 = _dbus_string_get_const_data_len (buffer2, start2, len2); + else + { + data2 = NULL; + start2 = 0; + len2 = 0; + } + + vectors[0].iov_base = (char*) data1; + vectors[0].iov_len = len1; + vectors[1].iov_base = (char*) data2; + vectors[1].iov_len = len2; + + again: + + bytes_written = writev (fd, + vectors, + data2 ? 2 : 1); + + if (bytes_written < 0 && errno == EINTR) + goto again; + + return bytes_written; + } +#else /* HAVE_WRITEV */ + { + int ret1; + + ret1 = _dbus_write (fd, buffer1, start1, len1); + if (ret1 == len1 && buffer2 != NULL) + { + ret2 = _dbus_write (fd, buffer2, start2, len2); + if (ret2 < 0) + ret2 = 0; /* we can't report an error as the first write was OK */ + + return ret1 + ret2; + } + else + return ret1; + } +#endif /* !HAVE_WRITEV */ +} + +#define _DBUS_MAX_SUN_PATH_LENGTH 99 + +/** + * @def _DBUS_MAX_SUN_PATH_LENGTH + * + * Maximum length of the path to a UNIX domain socket, + * sockaddr_un::sun_path member. POSIX requires that all systems + * support at least 100 bytes here, including the nul termination. + * We use 99 for the max value to allow for the nul. + * + * We could probably also do sizeof (addr.sun_path) + * but this way we are the same on all platforms + * which is probably a good idea. + */ + +/** + * Creates a socket and connects it to the UNIX domain socket at the + * given path. The connection fd is returned, and is set up as + * nonblocking. + * + * Uses abstract sockets instead of filesystem-linked sockets if + * requested (it's possible only on Linux; see "man 7 unix" on Linux). + * On non-Linux abstract socket usage always fails. + * + * @param path the path to UNIX domain socket + * @param abstract #TRUE to use abstract namespace + * @param error return location for error code + * @returns connection file descriptor or -1 on error + */ +int +_dbus_connect_unix_socket (const char *path, + dbus_bool_t abstract, + DBusError *error) +{ + int fd; + size_t path_len; + struct sockaddr_un addr; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + _dbus_verbose ("connecting to unix socket %s abstract=%d\n", + path, abstract); + + + if (!_dbus_open_unix_socket (&fd, error)) + { + _DBUS_ASSERT_ERROR_IS_SET(error); + return -1; + } + _DBUS_ASSERT_ERROR_IS_CLEAR(error); + + _DBUS_ZERO (addr); + addr.sun_family = AF_UNIX; + path_len = strlen (path); + + if (abstract) + { +#ifdef HAVE_ABSTRACT_SOCKETS + addr.sun_path[0] = '\0'; /* this is what says "use abstract" */ + path_len++; /* Account for the extra nul byte added to the start of sun_path */ + + if (path_len > _DBUS_MAX_SUN_PATH_LENGTH) + { + dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, + "Abstract socket name too long\n"); + _dbus_close (fd, NULL); + return -1; + } + + strncpy (&addr.sun_path[1], path, path_len); + /* _dbus_verbose_bytes (addr.sun_path, sizeof (addr.sun_path)); */ +#else /* HAVE_ABSTRACT_SOCKETS */ + dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED, + "Operating system does not support abstract socket namespace\n"); + _dbus_close (fd, NULL); + return -1; +#endif /* ! HAVE_ABSTRACT_SOCKETS */ + } + else + { + if (path_len > _DBUS_MAX_SUN_PATH_LENGTH) + { + dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, + "Socket name too long\n"); + _dbus_close (fd, NULL); + return -1; + } + + strncpy (addr.sun_path, path, path_len); + } + + if (connect (fd, (struct sockaddr*) &addr, _DBUS_STRUCT_OFFSET (struct sockaddr_un, sun_path) + path_len) < 0) + { + dbus_set_error (error, + _dbus_error_from_errno (errno), + "Failed to connect to socket %s: %s", + path, _dbus_strerror (errno)); + + _dbus_close (fd, NULL); + fd = -1; + + return -1; + } + + if (!_dbus_set_fd_nonblocking (fd, error)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + + _dbus_close (fd, NULL); + fd = -1; + + return -1; + } + + return fd; +} + +/** + * Enables or disables the reception of credentials on the given socket during + * the next message transmission. This is only effective if the #LOCAL_CREDS + * system feature exists, in which case the other side of the connection does + * not have to do anything special to send the credentials. + * + * @param fd socket on which to change the #LOCAL_CREDS flag. + * @param on whether to enable or disable the #LOCAL_CREDS flag. + */ +static dbus_bool_t +_dbus_set_local_creds (int fd, dbus_bool_t on) +{ + dbus_bool_t retval = TRUE; + +#if defined(HAVE_CMSGCRED) + /* NOOP just to make sure only one codepath is used + * and to prefer CMSGCRED + */ +#elif defined(LOCAL_CREDS) + int val = on ? 1 : 0; + if (setsockopt (fd, 0, LOCAL_CREDS, &val, sizeof (val)) < 0) + { + _dbus_verbose ("Unable to set LOCAL_CREDS socket option on fd %d\n", fd); + retval = FALSE; + } + else + _dbus_verbose ("LOCAL_CREDS %s for further messages on fd %d\n", + on ? "enabled" : "disabled", fd); +#endif + + return retval; +} + +/** + * Creates a socket and binds it to the given path, + * then listens on the socket. The socket is + * set to be nonblocking. + * + * Uses abstract sockets instead of filesystem-linked + * sockets if requested (it's possible only on Linux; + * see "man 7 unix" on Linux). + * On non-Linux abstract socket usage always fails. + * + * @param path the socket name + * @param abstract #TRUE to use abstract namespace + * @param error return location for errors + * @returns the listening file descriptor or -1 on error + */ +int +_dbus_listen_unix_socket (const char *path, + dbus_bool_t abstract, + DBusError *error) +{ + int listen_fd; + struct sockaddr_un addr; + size_t path_len; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + _dbus_verbose ("listening on unix socket %s abstract=%d\n", + path, abstract); + + if (!_dbus_open_unix_socket (&listen_fd, error)) + { + _DBUS_ASSERT_ERROR_IS_SET(error); + return -1; + } + _DBUS_ASSERT_ERROR_IS_CLEAR(error); + + _DBUS_ZERO (addr); + addr.sun_family = AF_UNIX; + path_len = strlen (path); + + if (abstract) + { +#ifdef HAVE_ABSTRACT_SOCKETS + /* remember that abstract names aren't nul-terminated so we rely + * on sun_path being filled in with zeroes above. + */ + addr.sun_path[0] = '\0'; /* this is what says "use abstract" */ + path_len++; /* Account for the extra nul byte added to the start of sun_path */ + + if (path_len > _DBUS_MAX_SUN_PATH_LENGTH) + { + dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, + "Abstract socket name too long\n"); + _dbus_close (listen_fd, NULL); + return -1; + } + + strncpy (&addr.sun_path[1], path, path_len); + /* _dbus_verbose_bytes (addr.sun_path, sizeof (addr.sun_path)); */ +#else /* HAVE_ABSTRACT_SOCKETS */ + dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED, + "Operating system does not support abstract socket namespace\n"); + _dbus_close (listen_fd, NULL); + return -1; +#endif /* ! HAVE_ABSTRACT_SOCKETS */ + } + else + { + /* Discussed security implications of this with Nalin, + * and we couldn't think of where it would kick our ass, but + * it still seems a bit sucky. It also has non-security suckage; + * really we'd prefer to exit if the socket is already in use. + * But there doesn't seem to be a good way to do this. + * + * Just to be extra careful, I threw in the stat() - clearly + * the stat() can't *fix* any security issue, but it at least + * avoids inadvertent/accidental data loss. + */ + { + struct stat sb; + + if (stat (path, &sb) == 0 && + S_ISSOCK (sb.st_mode)) + unlink (path); + } + + if (path_len > _DBUS_MAX_SUN_PATH_LENGTH) + { + dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, + "Abstract socket name too long\n"); + _dbus_close (listen_fd, NULL); + return -1; + } + + strncpy (addr.sun_path, path, path_len); + } + + if (bind (listen_fd, (struct sockaddr*) &addr, _DBUS_STRUCT_OFFSET (struct sockaddr_un, sun_path) + path_len) < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to bind socket \"%s\": %s", + path, _dbus_strerror (errno)); + _dbus_close (listen_fd, NULL); + return -1; + } + + if (listen (listen_fd, 30 /* backlog */) < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to listen on socket \"%s\": %s", + path, _dbus_strerror (errno)); + _dbus_close (listen_fd, NULL); + return -1; + } + + if (!_dbus_set_local_creds (listen_fd, TRUE)) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to enable LOCAL_CREDS on socket \"%s\": %s", + path, _dbus_strerror (errno)); + close (listen_fd); + return -1; + } + + if (!_dbus_set_fd_nonblocking (listen_fd, error)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + _dbus_close (listen_fd, NULL); + return -1; + } + + /* Try opening up the permissions, but if we can't, just go ahead + * and continue, maybe it will be good enough. + */ + if (!abstract && chmod (path, 0777) < 0) + _dbus_warn ("Could not set mode 0777 on socket %s\n", + path); + + return listen_fd; +} + +/** + * Creates a socket and connects to a socket at the given host + * and port. The connection fd is returned, and is set up as + * nonblocking. + * + * @param host the host name to connect to + * @param port the port to connect to + * @param family the address family to listen on, NULL for all + * @param error return location for error code + * @returns connection file descriptor or -1 on error + */ +int +_dbus_connect_tcp_socket (const char *host, + const char *port, + const char *family, + DBusError *error) +{ + int fd = -1, res; + struct addrinfo hints; + struct addrinfo *ai, *tmp; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + if (!_dbus_open_tcp_socket (&fd, error)) + { + _DBUS_ASSERT_ERROR_IS_SET(error); + return -1; + } + + _DBUS_ASSERT_ERROR_IS_CLEAR(error); + + _DBUS_ZERO (hints); + + if (!family) + hints.ai_family = AF_UNSPEC; + else if (!strcmp(family, "ipv4")) + hints.ai_family = AF_INET; + else if (!strcmp(family, "ipv6")) + hints.ai_family = AF_INET6; + else + { + dbus_set_error (error, + _dbus_error_from_errno (errno), + "Unknown address family %s", family); + return -1; + } + hints.ai_protocol = IPPROTO_TCP; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_ADDRCONFIG; + + if ((res = getaddrinfo(host, port, &hints, &ai)) != 0) + { + dbus_set_error (error, + _dbus_error_from_errno (errno), + "Failed to lookup host/port: \"%s:%s\": %s (%d)", + host, port, gai_strerror(res), res); + _dbus_close (fd, NULL); + return -1; + } + + tmp = ai; + while (tmp) + { + if (!_dbus_open_socket (&fd, tmp->ai_family, SOCK_STREAM, 0, error)) + { + freeaddrinfo(ai); + _DBUS_ASSERT_ERROR_IS_SET(error); + return -1; + } + _DBUS_ASSERT_ERROR_IS_CLEAR(error); + + if (connect (fd, (struct sockaddr*) tmp->ai_addr, tmp->ai_addrlen) < 0) + { + _dbus_close(fd, NULL); + fd = -1; + tmp = tmp->ai_next; + continue; + } + + break; + } + freeaddrinfo(ai); + + if (fd == -1) + { + dbus_set_error (error, + _dbus_error_from_errno (errno), + "Failed to connect to socket \"%s:%s\" %s", + host, port, _dbus_strerror(errno)); + return -1; + } + + + if (!_dbus_set_fd_nonblocking (fd, error)) + { + _dbus_close (fd, NULL); + fd = -1; + + return -1; + } + + return fd; +} + +/** + * Creates a socket and binds it to the given path, then listens on + * the socket. The socket is set to be nonblocking. In case of port=0 + * a random free port is used and returned in the port parameter. + * If inaddr_any is specified, the hostname is ignored. + * + * @param host the host name to listen on + * @param port the port to listen on, if zero a free port will be used + * @param family the address family to listen on, NULL for all + * @param retport string to return the actual port listened on + * @param fds_p location to store returned file descriptors + * @param error return location for errors + * @returns the number of listening file descriptors or -1 on error + */ +int +_dbus_listen_tcp_socket (const char *host, + const char *port, + const char *family, + DBusString *retport, + int **fds_p, + DBusError *error) +{ + int nlisten_fd = 0, *listen_fd = NULL, res, i; + struct addrinfo hints; + struct addrinfo *ai, *tmp; + + *fds_p = NULL; + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + _DBUS_ZERO (hints); + + if (!family) + hints.ai_family = AF_UNSPEC; + else if (!strcmp(family, "ipv4")) + hints.ai_family = AF_INET; + else if (!strcmp(family, "ipv6")) + hints.ai_family = AF_INET6; + else + { + dbus_set_error (error, + _dbus_error_from_errno (errno), + "Unknown address family %s", family); + return -1; + } + + hints.ai_protocol = IPPROTO_TCP; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE; + + redo_lookup_with_port: + if ((res = getaddrinfo(host, port, &hints, &ai)) != 0 || !ai) + { + dbus_set_error (error, + _dbus_error_from_errno (errno), + "Failed to lookup host/port: \"%s:%s\": %s (%d)", + host ? host : "*", port, gai_strerror(res), res); + return -1; + } + + tmp = ai; + while (tmp) + { + int fd = -1, *newlisten_fd; + if (!_dbus_open_socket (&fd, tmp->ai_family, SOCK_STREAM, 0, error)) + { + _DBUS_ASSERT_ERROR_IS_SET(error); + goto failed; + } + _DBUS_ASSERT_ERROR_IS_CLEAR(error); + + if (bind (fd, (struct sockaddr*) tmp->ai_addr, tmp->ai_addrlen) < 0) + { + _dbus_close(fd, NULL); + if (errno == EADDRINUSE) + { + /* Depending on kernel policy, it may or may not + be neccessary to bind to both IPv4 & 6 addresses + so ignore EADDRINUSE here */ + tmp = tmp->ai_next; + continue; + } + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to bind socket \"%s:%s\": %s", + host ? host : "*", port, _dbus_strerror (errno)); + goto failed; + } + + if (listen (fd, 30 /* backlog */) < 0) + { + _dbus_close (fd, NULL); + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to listen on socket \"%s:%s\": %s", + host ? host : "*", port, _dbus_strerror (errno)); + goto failed; + } + + newlisten_fd = dbus_realloc(listen_fd, sizeof(int)*(nlisten_fd+1)); + if (!newlisten_fd) + { + _dbus_close (fd, NULL); + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to allocate file handle array: %s", + _dbus_strerror (errno)); + goto failed; + } + listen_fd = newlisten_fd; + listen_fd[nlisten_fd] = fd; + nlisten_fd++; + + if (!_dbus_string_get_length(retport)) + { + /* If the user didn't specify a port, or used 0, then + the kernel chooses a port. After the first address + is bound to, we need to force all remaining addresses + to use the same port */ + if (!port || !strcmp(port, "0")) + { + struct sockaddr_storage addr; + socklen_t addrlen; + char portbuf[50]; + + addrlen = sizeof(addr); + getsockname(fd, (struct sockaddr*) &addr, &addrlen); + + if ((res = getnameinfo((struct sockaddr*)&addr, addrlen, NULL, 0, + portbuf, sizeof(portbuf), + NI_NUMERICHOST)) != 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to resolve port \"%s:%s\": %s (%s)", + host ? host : "*", port, gai_strerror(res), res); + goto failed; + } + if (!_dbus_string_append(retport, portbuf)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto failed; + } + + /* Release current address list & redo lookup */ + port = _dbus_string_get_const_data(retport); + freeaddrinfo(ai); + goto redo_lookup_with_port; + } + else + { + if (!_dbus_string_append(retport, port)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto failed; + } + } + } + + tmp = tmp->ai_next; + } + freeaddrinfo(ai); + ai = NULL; + + if (!nlisten_fd) + { + errno = EADDRINUSE; + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to bind socket \"%s:%s\": %s", + host ? host : "*", port, _dbus_strerror (errno)); + return -1; + } + + for (i = 0 ; i < nlisten_fd ; i++) + { + if (!_dbus_set_fd_nonblocking (listen_fd[i], error)) + { + goto failed; + } + } + + *fds_p = listen_fd; + + return nlisten_fd; + + failed: + if (ai) + freeaddrinfo(ai); + for (i = 0 ; i < nlisten_fd ; i++) + _dbus_close(listen_fd[i], NULL); + dbus_free(listen_fd); + return -1; +} + +static dbus_bool_t +write_credentials_byte (int server_fd, + DBusError *error) +{ + int bytes_written; + char buf[1] = { '\0' }; +#if defined(HAVE_CMSGCRED) + struct { + struct cmsghdr hdr; + struct cmsgcred cred; + } cmsg; + struct iovec iov; + struct msghdr msg; + iov.iov_base = buf; + iov.iov_len = 1; + + memset (&msg, 0, sizeof (msg)); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + msg.msg_control = &cmsg; + msg.msg_controllen = sizeof (cmsg); + memset (&cmsg, 0, sizeof (cmsg)); + cmsg.hdr.cmsg_len = sizeof (cmsg); + cmsg.hdr.cmsg_level = SOL_SOCKET; + cmsg.hdr.cmsg_type = SCM_CREDS; +#endif + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + again: + +#if defined(HAVE_CMSGCRED) + bytes_written = sendmsg (server_fd, &msg, 0); +#else + bytes_written = write (server_fd, buf, 1); +#endif + + if (bytes_written < 0 && errno == EINTR) + goto again; + + if (bytes_written < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to write credentials byte: %s", + _dbus_strerror (errno)); + return FALSE; + } + else if (bytes_written == 0) + { + dbus_set_error (error, DBUS_ERROR_IO_ERROR, + "wrote zero bytes writing credentials byte"); + return FALSE; + } + else + { + _dbus_assert (bytes_written == 1); + _dbus_verbose ("wrote credentials byte\n"); + return TRUE; + } +} + +/** + * Reads a single byte which must be nul (an error occurs otherwise), + * and reads unix credentials if available. Clears the credentials + * object, then adds pid/uid if available, so any previous credentials + * stored in the object are lost. + * + * Return value indicates whether a byte was read, not whether + * we got valid credentials. On some systems, such as Linux, + * reading/writing the byte isn't actually required, but we do it + * anyway just to avoid multiple codepaths. + * + * Fails if no byte is available, so you must select() first. + * + * The point of the byte is that on some systems we have to + * use sendmsg()/recvmsg() to transmit credentials. + * + * @param client_fd the client file descriptor + * @param credentials object to add client credentials to + * @param error location to store error code + * @returns #TRUE on success + */ +dbus_bool_t +_dbus_read_credentials_socket (int client_fd, + DBusCredentials *credentials, + DBusError *error) +{ + struct msghdr msg; + struct iovec iov; + char buf; + dbus_uid_t uid_read; + dbus_pid_t pid_read; + int bytes_read; + + uid_read = DBUS_UID_UNSET; + pid_read = DBUS_PID_UNSET; + +#ifdef HAVE_CMSGCRED + struct { + struct cmsghdr hdr; + struct cmsgcred cred; + } cmsg; + +#elif defined(LOCAL_CREDS) + struct { + struct cmsghdr hdr; + struct sockcred cred; + } cmsg; +#endif + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + /* The POSIX spec certainly doesn't promise this, but + * we need these assertions to fail as soon as we're wrong about + * it so we can do the porting fixups + */ + _dbus_assert (sizeof (pid_t) <= sizeof (dbus_pid_t)); + _dbus_assert (sizeof (uid_t) <= sizeof (dbus_uid_t)); + _dbus_assert (sizeof (gid_t) <= sizeof (dbus_gid_t)); + + _dbus_credentials_clear (credentials); + + /* Systems supporting LOCAL_CREDS are configured to have this feature + * enabled (if it does not conflict with HAVE_CMSGCRED) prior accepting + * the connection. Therefore, the received message must carry the + * credentials information without doing anything special. + */ + + iov.iov_base = &buf; + iov.iov_len = 1; + + memset (&msg, 0, sizeof (msg)); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + +#if defined(HAVE_CMSGCRED) || defined(LOCAL_CREDS) + memset (&cmsg, 0, sizeof (cmsg)); + msg.msg_control = &cmsg; + msg.msg_controllen = sizeof (cmsg); +#endif + + again: + bytes_read = recvmsg (client_fd, &msg, 0); + + if (bytes_read < 0) + { + if (errno == EINTR) + goto again; + + /* EAGAIN or EWOULDBLOCK would be unexpected here since we would + * normally only call read_credentials if the socket was ready + * for reading + */ + + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to read credentials byte: %s", + _dbus_strerror (errno)); + return FALSE; + } + else if (bytes_read == 0) + { + /* this should not happen unless we are using recvmsg wrong, + * so is essentially here for paranoia + */ + dbus_set_error (error, DBUS_ERROR_FAILED, + "Failed to read credentials byte (zero-length read)"); + return FALSE; + } + else if (buf != '\0') + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Credentials byte was not nul"); + return FALSE; + } + +#if defined(HAVE_CMSGCRED) || defined(LOCAL_CREDS) + if (cmsg.hdr.cmsg_len < sizeof (cmsg) || cmsg.hdr.cmsg_type != SCM_CREDS) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Message from recvmsg() was not SCM_CREDS"); + return FALSE; + } +#endif + + _dbus_verbose ("read credentials byte\n"); + + { +#ifdef SO_PEERCRED + struct ucred cr; + int cr_len = sizeof (cr); + + if (getsockopt (client_fd, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) == 0 && + cr_len == sizeof (cr)) + { + pid_read = cr.pid; + uid_read = cr.uid; + } + else + { + _dbus_verbose ("Failed to getsockopt() credentials, returned len %d/%d: %s\n", + cr_len, (int) sizeof (cr), _dbus_strerror (errno)); + } +#elif defined(HAVE_CMSGCRED) + pid_read = cmsg.cred.cmcred_pid; + uid_read = cmsg.cred.cmcred_euid; +#elif defined(LOCAL_CREDS) + pid_read = DBUS_PID_UNSET; + uid_read = cmsg.cred.sc_uid; + /* Since we have already got the credentials from this socket, we can + * disable its LOCAL_CREDS flag if it was ever set. */ + _dbus_set_local_creds (client_fd, FALSE); +#elif defined(HAVE_GETPEEREID) + uid_t euid; + gid_t egid; + if (getpeereid (client_fd, &euid, &egid) == 0) + { + uid_read = euid; + } + else + { + _dbus_verbose ("Failed to getpeereid() credentials: %s\n", _dbus_strerror (errno)); + } +#elif defined(HAVE_GETPEERUCRED) + ucred_t * ucred = NULL; + if (getpeerucred (client_fd, &ucred) == 0) + { + pid_read = ucred_getpid (ucred); + uid_read = ucred_geteuid (ucred); +#ifdef HAVE_ADT + /* generate audit session data based on socket ucred */ + adt_session_data_t *adth = NULL; + adt_export_data_t *data = NULL; + size_t size = 0; + if (adt_start_session (&adth, NULL, 0) || (adth == NULL)) + { + _dbus_verbose ("Failed to adt_start_session(): %s\n", _dbus_strerror (errno)); + } + else + { + if (adt_set_from_ucred (adth, ucred, ADT_NEW)) + { + _dbus_verbose ("Failed to adt_set_from_ucred(): %s\n", _dbus_strerror (errno)); + } + else + { + size = adt_export_session_data (adth, &data); + if (size <= 0) + { + _dbus_verbose ("Failed to adt_export_session_data(): %s\n", _dbus_strerror (errno)); + } + else + { + _dbus_credentials_add_adt_audit_data (credentials, data, size); + free (data); + } + } + (void) adt_end_session (adth); + } +#endif /* HAVE_ADT */ + } + else + { + _dbus_verbose ("Failed to getpeerucred() credentials: %s\n", _dbus_strerror (errno)); + } + if (ucred != NULL) + ucred_free (ucred); +#else /* !SO_PEERCRED && !HAVE_CMSGCRED && !HAVE_GETPEEREID && !HAVE_GETPEERUCRED */ + _dbus_verbose ("Socket credentials not supported on this OS\n"); +#endif + } + + _dbus_verbose ("Credentials:" + " pid "DBUS_PID_FORMAT + " uid "DBUS_UID_FORMAT + "\n", + pid_read, + uid_read); + + if (pid_read != DBUS_PID_UNSET) + { + if (!_dbus_credentials_add_unix_pid (credentials, pid_read)) + { + _DBUS_SET_OOM (error); + return FALSE; + } + } + + if (uid_read != DBUS_UID_UNSET) + { + if (!_dbus_credentials_add_unix_uid (credentials, uid_read)) + { + _DBUS_SET_OOM (error); + return FALSE; + } + } + + return TRUE; +} + +/** + * Sends a single nul byte with our UNIX credentials as ancillary + * data. Returns #TRUE if the data was successfully written. On + * systems that don't support sending credentials, just writes a byte, + * doesn't send any credentials. On some systems, such as Linux, + * reading/writing the byte isn't actually required, but we do it + * anyway just to avoid multiple codepaths. + * + * Fails if no byte can be written, so you must select() first. + * + * The point of the byte is that on some systems we have to + * use sendmsg()/recvmsg() to transmit credentials. + * + * @param server_fd file descriptor for connection to server + * @param error return location for error code + * @returns #TRUE if the byte was sent + */ +dbus_bool_t +_dbus_send_credentials_socket (int server_fd, + DBusError *error) +{ + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + if (write_credentials_byte (server_fd, error)) + return TRUE; + else + return FALSE; +} + +/** + * Accepts a connection on a listening socket. + * Handles EINTR for you. + * + * @param listen_fd the listen file descriptor + * @returns the connection fd of the client, or -1 on error + */ +int +_dbus_accept (int listen_fd) +{ + int client_fd; + struct sockaddr addr; + socklen_t addrlen; + + addrlen = sizeof (addr); + + retry: + client_fd = accept (listen_fd, &addr, &addrlen); + + if (client_fd < 0) + { + if (errno == EINTR) + goto retry; + } + + _dbus_verbose ("client fd %d accepted\n", client_fd); + + return client_fd; +} + +/** + * Checks to make sure the given directory is + * private to the user + * + * @param dir the name of the directory + * @param error error return + * @returns #FALSE on failure + **/ +dbus_bool_t +_dbus_check_dir_is_private_to_user (DBusString *dir, DBusError *error) +{ + const char *directory; + struct stat sb; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + directory = _dbus_string_get_const_data (dir); + + if (stat (directory, &sb) < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "%s", _dbus_strerror (errno)); + + return FALSE; + } + + if ((S_IROTH & sb.st_mode) || (S_IWOTH & sb.st_mode) || + (S_IRGRP & sb.st_mode) || (S_IWGRP & sb.st_mode)) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "%s directory is not private to the user", directory); + return FALSE; + } + + return TRUE; +} + +static dbus_bool_t +fill_user_info_from_passwd (struct passwd *p, + DBusUserInfo *info, + DBusError *error) +{ + _dbus_assert (p->pw_name != NULL); + _dbus_assert (p->pw_dir != NULL); + + info->uid = p->pw_uid; + info->primary_gid = p->pw_gid; + info->username = _dbus_strdup (p->pw_name); + info->homedir = _dbus_strdup (p->pw_dir); + + if (info->username == NULL || + info->homedir == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return FALSE; + } + + return TRUE; +} + +static dbus_bool_t +fill_user_info (DBusUserInfo *info, + dbus_uid_t uid, + const DBusString *username, + DBusError *error) +{ + const char *username_c; + + /* exactly one of username/uid provided */ + _dbus_assert (username != NULL || uid != DBUS_UID_UNSET); + _dbus_assert (username == NULL || uid == DBUS_UID_UNSET); + + info->uid = DBUS_UID_UNSET; + info->primary_gid = DBUS_GID_UNSET; + info->group_ids = NULL; + info->n_group_ids = 0; + info->username = NULL; + info->homedir = NULL; + + if (username != NULL) + username_c = _dbus_string_get_const_data (username); + else + username_c = NULL; + + /* For now assuming that the getpwnam() and getpwuid() flavors + * are always symmetrical, if not we have to add more configure + * checks + */ + +#if defined (HAVE_POSIX_GETPWNAM_R) || defined (HAVE_NONPOSIX_GETPWNAM_R) + { + struct passwd *p; + int result; + size_t buflen; + char *buf; + struct passwd p_str; + + /* retrieve maximum needed size for buf */ + buflen = sysconf (_SC_GETPW_R_SIZE_MAX); + + /* sysconf actually returns a long, but everything else expects size_t, + * so just recast here. + * https://bugs.freedesktop.org/show_bug.cgi?id=17061 + */ + if ((long) buflen <= 0) + buflen = 1024; + + result = -1; + while (1) + { + buf = dbus_malloc (buflen); + if (buf == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return FALSE; + } + + p = NULL; +#ifdef HAVE_POSIX_GETPWNAM_R + if (uid != DBUS_UID_UNSET) + result = getpwuid_r (uid, &p_str, buf, buflen, + &p); + else + result = getpwnam_r (username_c, &p_str, buf, buflen, + &p); +#else + if (uid != DBUS_UID_UNSET) + p = getpwuid_r (uid, &p_str, buf, buflen); + else + p = getpwnam_r (username_c, &p_str, buf, buflen); + result = 0; +#endif /* !HAVE_POSIX_GETPWNAM_R */ + //Try a bigger buffer if ERANGE was returned + if (result == ERANGE && buflen < 512 * 1024) + { + dbus_free (buf); + buflen *= 2; + } + else + { + break; + } + } + if (result == 0 && p == &p_str) + { + if (!fill_user_info_from_passwd (p, info, error)) + { + dbus_free (buf); + return FALSE; + } + dbus_free (buf); + } + else + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "User \"%s\" unknown or no memory to allocate password entry\n", + username_c ? username_c : "???"); + _dbus_verbose ("User %s unknown\n", username_c ? username_c : "???"); + dbus_free (buf); + return FALSE; + } + } +#else /* ! HAVE_GETPWNAM_R */ + { + /* I guess we're screwed on thread safety here */ + struct passwd *p; + + if (uid != DBUS_UID_UNSET) + p = getpwuid (uid); + else + p = getpwnam (username_c); + + if (p != NULL) + { + if (!fill_user_info_from_passwd (p, info, error)) + { + return FALSE; + } + } + else + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "User \"%s\" unknown or no memory to allocate password entry\n", + username_c ? username_c : "???"); + _dbus_verbose ("User %s unknown\n", username_c ? username_c : "???"); + return FALSE; + } + } +#endif /* ! HAVE_GETPWNAM_R */ + + /* Fill this in so we can use it to get groups */ + username_c = info->username; + +#ifdef HAVE_GETGROUPLIST + { + gid_t *buf; + int buf_count; + int i; + + buf_count = 17; + buf = dbus_new (gid_t, buf_count); + if (buf == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto failed; + } + + if (getgrouplist (username_c, + info->primary_gid, + buf, &buf_count) < 0) + { + gid_t *new = dbus_realloc (buf, buf_count * sizeof (buf[0])); + if (new == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + dbus_free (buf); + goto failed; + } + + buf = new; + + errno = 0; + if (getgrouplist (username_c, info->primary_gid, buf, &buf_count) < 0) + { + dbus_set_error (error, + _dbus_error_from_errno (errno), + "Failed to get groups for username \"%s\" primary GID " + DBUS_GID_FORMAT ": %s\n", + username_c, info->primary_gid, + _dbus_strerror (errno)); + dbus_free (buf); + goto failed; + } + } + + info->group_ids = dbus_new (dbus_gid_t, buf_count); + if (info->group_ids == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + dbus_free (buf); + goto failed; + } + + for (i = 0; i < buf_count; ++i) + info->group_ids[i] = buf[i]; + + info->n_group_ids = buf_count; + + dbus_free (buf); + } +#else /* HAVE_GETGROUPLIST */ + { + /* We just get the one group ID */ + info->group_ids = dbus_new (dbus_gid_t, 1); + if (info->group_ids == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto failed; + } + + info->n_group_ids = 1; + + (info->group_ids)[0] = info->primary_gid; + } +#endif /* HAVE_GETGROUPLIST */ + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + return TRUE; + + failed: + _DBUS_ASSERT_ERROR_IS_SET (error); + return FALSE; +} + +/** + * Gets user info for the given username. + * + * @param info user info object to initialize + * @param username the username + * @param error error return + * @returns #TRUE on success + */ +dbus_bool_t +_dbus_user_info_fill (DBusUserInfo *info, + const DBusString *username, + DBusError *error) +{ + return fill_user_info (info, DBUS_UID_UNSET, + username, error); +} + +/** + * Gets user info for the given user ID. + * + * @param info user info object to initialize + * @param uid the user ID + * @param error error return + * @returns #TRUE on success + */ +dbus_bool_t +_dbus_user_info_fill_uid (DBusUserInfo *info, + dbus_uid_t uid, + DBusError *error) +{ + return fill_user_info (info, uid, + NULL, error); +} + +/** + * Adds the credentials of the current process to the + * passed-in credentials object. + * + * @param credentials credentials to add to + * @returns #FALSE if no memory; does not properly roll back on failure, so only some credentials may have been added + */ +dbus_bool_t +_dbus_credentials_add_from_current_process (DBusCredentials *credentials) +{ + /* The POSIX spec certainly doesn't promise this, but + * we need these assertions to fail as soon as we're wrong about + * it so we can do the porting fixups + */ + _dbus_assert (sizeof (pid_t) <= sizeof (dbus_pid_t)); + _dbus_assert (sizeof (uid_t) <= sizeof (dbus_uid_t)); + _dbus_assert (sizeof (gid_t) <= sizeof (dbus_gid_t)); + + if (!_dbus_credentials_add_unix_pid(credentials, _dbus_getpid())) + return FALSE; + if (!_dbus_credentials_add_unix_uid(credentials, _dbus_geteuid())) + return FALSE; + + return TRUE; +} + +/** + * Append to the string the identity we would like to have when we + * authenticate, on UNIX this is the current process UID and on + * Windows something else, probably a Windows SID string. No escaping + * is required, that is done in dbus-auth.c. The username here + * need not be anything human-readable, it can be the machine-readable + * form i.e. a user id. + * + * @param str the string to append to + * @returns #FALSE on no memory + */ +dbus_bool_t +_dbus_append_user_from_current_process (DBusString *str) +{ + return _dbus_string_append_uint (str, + _dbus_geteuid ()); +} + +/** + * Gets our process ID + * @returns process ID + */ +dbus_pid_t +_dbus_getpid (void) +{ + return getpid (); +} + +/** Gets our UID + * @returns process UID + */ +dbus_uid_t +_dbus_getuid (void) +{ + return getuid (); +} + +/** Gets our effective UID + * @returns process effective UID + */ +dbus_uid_t +_dbus_geteuid (void) +{ + return geteuid (); +} + +/** + * The only reason this is separate from _dbus_getpid() is to allow it + * on Windows for logging but not for other purposes. + * + * @returns process ID to put in log messages + */ +unsigned long +_dbus_pid_for_log (void) +{ + return getpid (); +} + +/** + * Gets a UID from a UID string. + * + * @param uid_str the UID in string form + * @param uid UID to fill in + * @returns #TRUE if successfully filled in UID + */ +dbus_bool_t +_dbus_parse_uid (const DBusString *uid_str, + dbus_uid_t *uid) +{ + int end; + long val; + + if (_dbus_string_get_length (uid_str) == 0) + { + _dbus_verbose ("UID string was zero length\n"); + return FALSE; + } + + val = -1; + end = 0; + if (!_dbus_string_parse_int (uid_str, 0, &val, + &end)) + { + _dbus_verbose ("could not parse string as a UID\n"); + return FALSE; + } + + if (end != _dbus_string_get_length (uid_str)) + { + _dbus_verbose ("string contained trailing stuff after UID\n"); + return FALSE; + } + + *uid = val; + + return TRUE; +} + + +_DBUS_DEFINE_GLOBAL_LOCK (atomic); + +#if DBUS_USE_ATOMIC_INT_486_COND +/* Taken from CVS version 1.7 of glibc's sysdeps/i386/i486/atomicity.h */ +/* Since the asm stuff here is gcc-specific we go ahead and use "inline" also */ +static inline dbus_int32_t +atomic_exchange_and_add (DBusAtomic *atomic, + volatile dbus_int32_t val) +{ + register dbus_int32_t result; + + __asm__ __volatile__ ("lock; xaddl %0,%1" + : "=r" (result), "=m" (atomic->value) + : "0" (val), "m" (atomic->value)); + return result; +} +#endif + +/** + * Atomically increments an integer + * + * @param atomic pointer to the integer to increment + * @returns the value before incrementing + * + * @todo implement arch-specific faster atomic ops + */ +dbus_int32_t +_dbus_atomic_inc (DBusAtomic *atomic) +{ +#if DBUS_USE_ATOMIC_INT_486_COND + return atomic_exchange_and_add (atomic, 1); +#else + dbus_int32_t res; + _DBUS_LOCK (atomic); + res = atomic->value; + atomic->value += 1; + _DBUS_UNLOCK (atomic); + return res; +#endif +} + +/** + * Atomically decrement an integer + * + * @param atomic pointer to the integer to decrement + * @returns the value before decrementing + * + * @todo implement arch-specific faster atomic ops + */ +dbus_int32_t +_dbus_atomic_dec (DBusAtomic *atomic) +{ +#if DBUS_USE_ATOMIC_INT_486_COND + return atomic_exchange_and_add (atomic, -1); +#else + dbus_int32_t res; + + _DBUS_LOCK (atomic); + res = atomic->value; + atomic->value -= 1; + _DBUS_UNLOCK (atomic); + return res; +#endif +} + +#ifdef DBUS_BUILD_TESTS +/** Gets our GID + * @returns process GID + */ +dbus_gid_t +_dbus_getgid (void) +{ + return getgid (); +} +#endif + +/** + * Wrapper for poll(). + * + * @param fds the file descriptors to poll + * @param n_fds number of descriptors in the array + * @param timeout_milliseconds timeout or -1 for infinite + * @returns numbers of fds with revents, or <0 on error + */ +int +_dbus_poll (DBusPollFD *fds, + int n_fds, + int timeout_milliseconds) +{ +#if defined(HAVE_POLL) && !defined(BROKEN_POLL) + /* This big thing is a constant expression and should get optimized + * out of existence. So it's more robust than a configure check at + * no cost. + */ + if (_DBUS_POLLIN == POLLIN && + _DBUS_POLLPRI == POLLPRI && + _DBUS_POLLOUT == POLLOUT && + _DBUS_POLLERR == POLLERR && + _DBUS_POLLHUP == POLLHUP && + _DBUS_POLLNVAL == POLLNVAL && + sizeof (DBusPollFD) == sizeof (struct pollfd) && + _DBUS_STRUCT_OFFSET (DBusPollFD, fd) == + _DBUS_STRUCT_OFFSET (struct pollfd, fd) && + _DBUS_STRUCT_OFFSET (DBusPollFD, events) == + _DBUS_STRUCT_OFFSET (struct pollfd, events) && + _DBUS_STRUCT_OFFSET (DBusPollFD, revents) == + _DBUS_STRUCT_OFFSET (struct pollfd, revents)) + { + return poll ((struct pollfd*) fds, + n_fds, + timeout_milliseconds); + } + else + { + /* We have to convert the DBusPollFD to an array of + * struct pollfd, poll, and convert back. + */ + _dbus_warn ("didn't implement poll() properly for this system yet\n"); + return -1; + } +#else /* ! HAVE_POLL */ + + fd_set read_set, write_set, err_set; + int max_fd = 0; + int i; + struct timeval tv; + int ready; + + FD_ZERO (&read_set); + FD_ZERO (&write_set); + FD_ZERO (&err_set); + + for (i = 0; i < n_fds; i++) + { + DBusPollFD *fdp = &fds[i]; + + if (fdp->events & _DBUS_POLLIN) + FD_SET (fdp->fd, &read_set); + + if (fdp->events & _DBUS_POLLOUT) + FD_SET (fdp->fd, &write_set); + + FD_SET (fdp->fd, &err_set); + + max_fd = MAX (max_fd, fdp->fd); + } + + tv.tv_sec = timeout_milliseconds / 1000; + tv.tv_usec = (timeout_milliseconds % 1000) * 1000; + + ready = select (max_fd + 1, &read_set, &write_set, &err_set, + timeout_milliseconds < 0 ? NULL : &tv); + + if (ready > 0) + { + for (i = 0; i < n_fds; i++) + { + DBusPollFD *fdp = &fds[i]; + + fdp->revents = 0; + + if (FD_ISSET (fdp->fd, &read_set)) + fdp->revents |= _DBUS_POLLIN; + + if (FD_ISSET (fdp->fd, &write_set)) + fdp->revents |= _DBUS_POLLOUT; + + if (FD_ISSET (fdp->fd, &err_set)) + fdp->revents |= _DBUS_POLLERR; + } + } + + return ready; +#endif +} + +/** + * Get current time, as in gettimeofday(). + * + * @param tv_sec return location for number of seconds + * @param tv_usec return location for number of microseconds (thousandths) + */ +void +_dbus_get_current_time (long *tv_sec, + long *tv_usec) +{ + struct timeval t; + + gettimeofday (&t, NULL); + + if (tv_sec) + *tv_sec = t.tv_sec; + if (tv_usec) + *tv_usec = t.tv_usec; +} + +/** + * Appends the contents of the given file to the string, + * returning error code. At the moment, won't open a file + * more than a megabyte in size. + * + * @param str the string to append to + * @param filename filename to load + * @param error place to set an error + * @returns #FALSE if error was set + */ +dbus_bool_t +_dbus_file_get_contents (DBusString *str, + const DBusString *filename, + DBusError *error) +{ + int fd; + struct stat sb; + int orig_len; + int total; + const char *filename_c; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + filename_c = _dbus_string_get_const_data (filename); + + /* O_BINARY useful on Cygwin */ + fd = open (filename_c, O_RDONLY | O_BINARY); + if (fd < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to open \"%s\": %s", + filename_c, + _dbus_strerror (errno)); + return FALSE; + } + + _dbus_verbose ("file fd %d opened\n", fd); + + if (fstat (fd, &sb) < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to stat \"%s\": %s", + filename_c, + _dbus_strerror (errno)); + + _dbus_verbose ("fstat() failed: %s", + _dbus_strerror (errno)); + + _dbus_close (fd, NULL); + + return FALSE; + } + + if (sb.st_size > _DBUS_ONE_MEGABYTE) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "File size %lu of \"%s\" is too large.", + (unsigned long) sb.st_size, filename_c); + _dbus_close (fd, NULL); + return FALSE; + } + + total = 0; + orig_len = _dbus_string_get_length (str); + if (sb.st_size > 0 && S_ISREG (sb.st_mode)) + { + int bytes_read; + + while (total < (int) sb.st_size) + { + bytes_read = _dbus_read (fd, str, + sb.st_size - total); + if (bytes_read <= 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Error reading \"%s\": %s", + filename_c, + _dbus_strerror (errno)); + + _dbus_verbose ("read() failed: %s", + _dbus_strerror (errno)); + + _dbus_close (fd, NULL); + _dbus_string_set_length (str, orig_len); + return FALSE; + } + else + total += bytes_read; + } + + _dbus_close (fd, NULL); + return TRUE; + } + else if (sb.st_size != 0) + { + _dbus_verbose ("Can only open regular files at the moment.\n"); + dbus_set_error (error, DBUS_ERROR_FAILED, + "\"%s\" is not a regular file", + filename_c); + _dbus_close (fd, NULL); + return FALSE; + } + else + { + _dbus_close (fd, NULL); + return TRUE; + } +} + +/** + * Writes a string out to a file. If the file exists, + * it will be atomically overwritten by the new data. + * + * @param str the string to write out + * @param filename the file to save string to + * @param error error to be filled in on failure + * @returns #FALSE on failure + */ +dbus_bool_t +_dbus_string_save_to_file (const DBusString *str, + const DBusString *filename, + DBusError *error) +{ + int fd; + int bytes_to_write; + const char *filename_c; + DBusString tmp_filename; + const char *tmp_filename_c; + int total; + dbus_bool_t need_unlink; + dbus_bool_t retval; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + fd = -1; + retval = FALSE; + need_unlink = FALSE; + + if (!_dbus_string_init (&tmp_filename)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return FALSE; + } + + if (!_dbus_string_copy (filename, 0, &tmp_filename, 0)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + _dbus_string_free (&tmp_filename); + return FALSE; + } + + if (!_dbus_string_append (&tmp_filename, ".")) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + _dbus_string_free (&tmp_filename); + return FALSE; + } + +#define N_TMP_FILENAME_RANDOM_BYTES 8 + if (!_dbus_generate_random_ascii (&tmp_filename, N_TMP_FILENAME_RANDOM_BYTES)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + _dbus_string_free (&tmp_filename); + return FALSE; + } + + filename_c = _dbus_string_get_const_data (filename); + tmp_filename_c = _dbus_string_get_const_data (&tmp_filename); + + fd = open (tmp_filename_c, O_WRONLY | O_BINARY | O_EXCL | O_CREAT, + 0600); + if (fd < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Could not create %s: %s", tmp_filename_c, + _dbus_strerror (errno)); + goto out; + } + + _dbus_verbose ("tmp file fd %d opened\n", fd); + + need_unlink = TRUE; + + total = 0; + bytes_to_write = _dbus_string_get_length (str); + + while (total < bytes_to_write) + { + int bytes_written; + + bytes_written = _dbus_write (fd, str, total, + bytes_to_write - total); + + if (bytes_written <= 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Could not write to %s: %s", tmp_filename_c, + _dbus_strerror (errno)); + + goto out; + } + + total += bytes_written; + } + + if (fsync(fd)) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Could not synchronize file %s: %s", + tmp_filename_c, _dbus_strerror (errno)); + + goto out; + } + + if (!_dbus_close (fd, NULL)) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Could not close file %s: %s", + tmp_filename_c, _dbus_strerror (errno)); + + goto out; + } + + fd = -1; + + if (rename (tmp_filename_c, filename_c) < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Could not rename %s to %s: %s", + tmp_filename_c, filename_c, + _dbus_strerror (errno)); + + goto out; + } + + need_unlink = FALSE; + + retval = TRUE; + + out: + /* close first, then unlink, to prevent ".nfs34234235" garbage + * files + */ + + if (fd >= 0) + _dbus_close (fd, NULL); + + if (need_unlink && unlink (tmp_filename_c) < 0) + _dbus_verbose ("Failed to unlink temp file %s: %s\n", + tmp_filename_c, _dbus_strerror (errno)); + + _dbus_string_free (&tmp_filename); + + if (!retval) + _DBUS_ASSERT_ERROR_IS_SET (error); + + return retval; +} + +/** Makes the file readable by every user in the system. + * + * @param filename the filename + * @param error error location + * @returns #TRUE if the file's permissions could be changed. + */ +dbus_bool_t +_dbus_make_file_world_readable(const DBusString *filename, + DBusError *error) +{ + const char *filename_c; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + filename_c = _dbus_string_get_const_data (filename); + if (chmod (filename_c, 0644) == -1) + { + dbus_set_error (error, + DBUS_ERROR_FAILED, + "Could not change permissions of file %s: %s\n", + filename_c, + _dbus_strerror (errno)); + return FALSE; + } + return TRUE; +} + +/** Creates the given file, failing if the file already exists. + * + * @param filename the filename + * @param error error location + * @returns #TRUE if we created the file and it didn't exist + */ +dbus_bool_t +_dbus_create_file_exclusively (const DBusString *filename, + DBusError *error) +{ + int fd; + const char *filename_c; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + filename_c = _dbus_string_get_const_data (filename); + + fd = open (filename_c, O_WRONLY | O_BINARY | O_EXCL | O_CREAT, + 0600); + if (fd < 0) + { + dbus_set_error (error, + DBUS_ERROR_FAILED, + "Could not create file %s: %s\n", + filename_c, + _dbus_strerror (errno)); + return FALSE; + } + + _dbus_verbose ("exclusive file fd %d opened\n", fd); + + if (!_dbus_close (fd, NULL)) + { + dbus_set_error (error, + DBUS_ERROR_FAILED, + "Could not close file %s: %s\n", + filename_c, + _dbus_strerror (errno)); + return FALSE; + } + + return TRUE; +} + +/** + * Deletes the given file. + * + * @param filename the filename + * @param error error location + * + * @returns #TRUE if unlink() succeeded + */ +dbus_bool_t +_dbus_delete_file (const DBusString *filename, + DBusError *error) +{ + const char *filename_c; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + filename_c = _dbus_string_get_const_data (filename); + + if (unlink (filename_c) < 0) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Failed to delete file %s: %s\n", + filename_c, _dbus_strerror (errno)); + return FALSE; + } + else + return TRUE; +} + +/** + * Creates a directory; succeeds if the directory + * is created or already existed. + * + * @param filename directory filename + * @param error initialized error object + * @returns #TRUE on success + */ +dbus_bool_t +_dbus_create_directory (const DBusString *filename, + DBusError *error) +{ + const char *filename_c; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + filename_c = _dbus_string_get_const_data (filename); + + if (mkdir (filename_c, 0700) < 0) + { + if (errno == EEXIST) + return TRUE; + + dbus_set_error (error, DBUS_ERROR_FAILED, + "Failed to create directory %s: %s\n", + filename_c, _dbus_strerror (errno)); + return FALSE; + } + else + return TRUE; +} + +/** + * Appends the given filename to the given directory. + * + * @todo it might be cute to collapse multiple '/' such as "foo//" + * concat "//bar" + * + * @param dir the directory name + * @param next_component the filename + * @returns #TRUE on success + */ +dbus_bool_t +_dbus_concat_dir_and_file (DBusString *dir, + const DBusString *next_component) +{ + dbus_bool_t dir_ends_in_slash; + dbus_bool_t file_starts_with_slash; + + if (_dbus_string_get_length (dir) == 0 || + _dbus_string_get_length (next_component) == 0) + return TRUE; + + dir_ends_in_slash = '/' == _dbus_string_get_byte (dir, + _dbus_string_get_length (dir) - 1); + + file_starts_with_slash = '/' == _dbus_string_get_byte (next_component, 0); + + if (dir_ends_in_slash && file_starts_with_slash) + { + _dbus_string_shorten (dir, 1); + } + else if (!(dir_ends_in_slash || file_starts_with_slash)) + { + if (!_dbus_string_append_byte (dir, '/')) + return FALSE; + } + + return _dbus_string_copy (next_component, 0, dir, + _dbus_string_get_length (dir)); +} + +/** nanoseconds in a second */ +#define NANOSECONDS_PER_SECOND 1000000000 +/** microseconds in a second */ +#define MICROSECONDS_PER_SECOND 1000000 +/** milliseconds in a second */ +#define MILLISECONDS_PER_SECOND 1000 +/** nanoseconds in a millisecond */ +#define NANOSECONDS_PER_MILLISECOND 1000000 +/** microseconds in a millisecond */ +#define MICROSECONDS_PER_MILLISECOND 1000 + +/** + * Sleeps the given number of milliseconds. + * @param milliseconds number of milliseconds + */ +void +_dbus_sleep_milliseconds (int milliseconds) +{ +#ifdef HAVE_NANOSLEEP + struct timespec req; + struct timespec rem; + + req.tv_sec = milliseconds / MILLISECONDS_PER_SECOND; + req.tv_nsec = (milliseconds % MILLISECONDS_PER_SECOND) * NANOSECONDS_PER_MILLISECOND; + rem.tv_sec = 0; + rem.tv_nsec = 0; + + while (nanosleep (&req, &rem) < 0 && errno == EINTR) + req = rem; +#elif defined (HAVE_USLEEP) + usleep (milliseconds * MICROSECONDS_PER_MILLISECOND); +#else /* ! HAVE_USLEEP */ + sleep (MAX (milliseconds / 1000, 1)); +#endif +} + +static dbus_bool_t +_dbus_generate_pseudorandom_bytes (DBusString *str, + int n_bytes) +{ + int old_len; + char *p; + + old_len = _dbus_string_get_length (str); + + if (!_dbus_string_lengthen (str, n_bytes)) + return FALSE; + + p = _dbus_string_get_data_len (str, old_len, n_bytes); + + _dbus_generate_pseudorandom_bytes_buffer (p, n_bytes); + + return TRUE; +} + +/** + * Generates the given number of random bytes, + * using the best mechanism we can come up with. + * + * @param str the string + * @param n_bytes the number of random bytes to append to string + * @returns #TRUE on success, #FALSE if no memory + */ +dbus_bool_t +_dbus_generate_random_bytes (DBusString *str, + int n_bytes) +{ + int old_len; + int fd; + + /* FALSE return means "no memory", if it could + * mean something else then we'd need to return + * a DBusError. So we always fall back to pseudorandom + * if the I/O fails. + */ + + old_len = _dbus_string_get_length (str); + fd = -1; + + /* note, urandom on linux will fall back to pseudorandom */ + fd = open ("/dev/urandom", O_RDONLY); + if (fd < 0) + return _dbus_generate_pseudorandom_bytes (str, n_bytes); + + _dbus_verbose ("/dev/urandom fd %d opened\n", fd); + + if (_dbus_read (fd, str, n_bytes) != n_bytes) + { + _dbus_close (fd, NULL); + _dbus_string_set_length (str, old_len); + return _dbus_generate_pseudorandom_bytes (str, n_bytes); + } + + _dbus_verbose ("Read %d bytes from /dev/urandom\n", + n_bytes); + + _dbus_close (fd, NULL); + + return TRUE; +} + +/** + * Exit the process, returning the given value. + * + * @param code the exit code + */ +void +_dbus_exit (int code) +{ + _exit (code); +} + +/** + * A wrapper around strerror() because some platforms + * may be lame and not have strerror(). Also, never + * returns NULL. + * + * @param error_number errno. + * @returns error description. + */ +const char* +_dbus_strerror (int error_number) +{ + const char *msg; + + msg = strerror (error_number); + if (msg == NULL) + msg = "unknown"; + + return msg; +} + +/** + * signal (SIGPIPE, SIG_IGN); + */ +void +_dbus_disable_sigpipe (void) +{ + signal (SIGPIPE, SIG_IGN); +} + +/** + * Sets the file descriptor to be close + * on exec. Should be called for all file + * descriptors in D-Bus code. + * + * @param fd the file descriptor + */ +void +_dbus_fd_set_close_on_exec (int fd) +{ + int val; + + val = fcntl (fd, F_GETFD, 0); + + if (val < 0) + return; + + val |= FD_CLOEXEC; + + fcntl (fd, F_SETFD, val); +} + +/** + * Closes a file descriptor. + * + * @param fd the file descriptor + * @param error error object + * @returns #FALSE if error set + */ +dbus_bool_t +_dbus_close (int fd, + DBusError *error) +{ + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + again: + if (close (fd) < 0) + { + if (errno == EINTR) + goto again; + + dbus_set_error (error, _dbus_error_from_errno (errno), + "Could not close fd %d", fd); + return FALSE; + } + + return TRUE; +} + +/** + * Sets a file descriptor to be nonblocking. + * + * @param fd the file descriptor. + * @param error address of error location. + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_set_fd_nonblocking (int fd, + DBusError *error) +{ + int val; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + val = fcntl (fd, F_GETFL, 0); + if (val < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to get flags from file descriptor %d: %s", + fd, _dbus_strerror (errno)); + _dbus_verbose ("Failed to get flags for fd %d: %s\n", fd, + _dbus_strerror (errno)); + return FALSE; + } + + if (fcntl (fd, F_SETFL, val | O_NONBLOCK) < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to set nonblocking flag of file descriptor %d: %s", + fd, _dbus_strerror (errno)); + _dbus_verbose ("Failed to set fd %d nonblocking: %s\n", + fd, _dbus_strerror (errno)); + + return FALSE; + } + + return TRUE; +} + +/** + * On GNU libc systems, print a crude backtrace to stderr. On other + * systems, print "no backtrace support" and block for possible gdb + * attachment if an appropriate environment variable is set. + */ +void +_dbus_print_backtrace (void) +{ +#if defined (HAVE_BACKTRACE) && defined (DBUS_BUILT_R_DYNAMIC) + void *bt[500]; + int bt_size; + int i; + char **syms; + + bt_size = backtrace (bt, 500); + + syms = backtrace_symbols (bt, bt_size); + + i = 0; + while (i < bt_size) + { + /* don't use dbus_warn since it can _dbus_abort() */ + fprintf (stderr, " %s\n", syms[i]); + ++i; + } + fflush (stderr); + + free (syms); +#elif defined (HAVE_BACKTRACE) && ! defined (DBUS_BUILT_R_DYNAMIC) + fprintf (stderr, " D-Bus not built with -rdynamic so unable to print a backtrace\n"); +#else + fprintf (stderr, " D-Bus not compiled with backtrace support so unable to print a backtrace\n"); +#endif +} + +/** + * Creates a full-duplex pipe (as in socketpair()). + * Sets both ends of the pipe nonblocking. + * + * @todo libdbus only uses this for the debug-pipe server, so in + * principle it could be in dbus-sysdeps-util.c, except that + * dbus-sysdeps-util.c isn't in libdbus when tests are enabled and the + * debug-pipe server is used. + * + * @param fd1 return location for one end + * @param fd2 return location for the other end + * @param blocking #TRUE if pipe should be blocking + * @param error error return + * @returns #FALSE on failure (if error is set) + */ +dbus_bool_t +_dbus_full_duplex_pipe (int *fd1, + int *fd2, + dbus_bool_t blocking, + DBusError *error) +{ +#ifdef HAVE_SOCKETPAIR + int fds[2]; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + if (socketpair (AF_UNIX, SOCK_STREAM, 0, fds) < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Could not create full-duplex pipe"); + return FALSE; + } + + if (!blocking && + (!_dbus_set_fd_nonblocking (fds[0], NULL) || + !_dbus_set_fd_nonblocking (fds[1], NULL))) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Could not set full-duplex pipe nonblocking"); + + _dbus_close (fds[0], NULL); + _dbus_close (fds[1], NULL); + + return FALSE; + } + + *fd1 = fds[0]; + *fd2 = fds[1]; + + _dbus_verbose ("full-duplex pipe %d <-> %d\n", + *fd1, *fd2); + + return TRUE; +#else + _dbus_warn ("_dbus_full_duplex_pipe() not implemented on this OS\n"); + dbus_set_error (error, DBUS_ERROR_FAILED, + "_dbus_full_duplex_pipe() not implemented on this OS"); + return FALSE; +#endif +} + +/** + * Measure the length of the given format string and arguments, + * not including the terminating nul. + * + * @param format a printf-style format string + * @param args arguments for the format string + * @returns length of the given format string and args + */ +int +_dbus_printf_string_upper_bound (const char *format, + va_list args) +{ + char c; + return vsnprintf (&c, 1, format, args); +} + +/** + * Gets the temporary files directory by inspecting the environment variables + * TMPDIR, TMP, and TEMP in that order. If none of those are set "/tmp" is returned + * + * @returns location of temp directory + */ +const char* +_dbus_get_tmpdir(void) +{ + static const char* tmpdir = NULL; + + if (tmpdir == NULL) + { + /* TMPDIR is what glibc uses, then + * glibc falls back to the P_tmpdir macro which + * just expands to "/tmp" + */ + if (tmpdir == NULL) + tmpdir = getenv("TMPDIR"); + + /* These two env variables are probably + * broken, but maybe some OS uses them? + */ + if (tmpdir == NULL) + tmpdir = getenv("TMP"); + if (tmpdir == NULL) + tmpdir = getenv("TEMP"); + + /* And this is the sane fallback. */ + if (tmpdir == NULL) + tmpdir = "/tmp"; + } + + _dbus_assert(tmpdir != NULL); + + return tmpdir; +} + +/** + * Determines the address of the session bus by querying a + * platform-specific method. + * + * If successful, returns #TRUE and appends the address to @p + * address. If a failure happens, returns #FALSE and + * sets an error in @p error. + * + * @param address a DBusString where the address can be stored + * @param error a DBusError to store the error in case of failure + * @returns #TRUE on success, #FALSE if an error happened + */ +dbus_bool_t +_dbus_get_autolaunch_address (DBusString *address, + DBusError *error) +{ + static char *argv[6]; + int address_pipe[2] = { -1, -1 }; + int errors_pipe[2] = { -1, -1 }; + pid_t pid; + int ret; + int status; + int orig_len; + int i; + DBusString uuid; + dbus_bool_t retval; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + retval = FALSE; + + if (!_dbus_string_init (&uuid)) + { + _DBUS_SET_OOM (error); + return FALSE; + } + + if (!_dbus_get_local_machine_uuid_encoded (&uuid)) + { + _DBUS_SET_OOM (error); + goto out; + } + + i = 0; + argv[i] = "dbus-launch"; + ++i; + argv[i] = "--autolaunch"; + ++i; + argv[i] = _dbus_string_get_data (&uuid); + ++i; + argv[i] = "--binary-syntax"; + ++i; + argv[i] = "--close-stderr"; + ++i; + argv[i] = NULL; + ++i; + + _dbus_assert (i == _DBUS_N_ELEMENTS (argv)); + + orig_len = _dbus_string_get_length (address); + +#define READ_END 0 +#define WRITE_END 1 + if (pipe (address_pipe) < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to create a pipe: %s", + _dbus_strerror (errno)); + _dbus_verbose ("Failed to create a pipe to call dbus-launch: %s\n", + _dbus_strerror (errno)); + goto out; + } + if (pipe (errors_pipe) < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to create a pipe: %s", + _dbus_strerror (errno)); + _dbus_verbose ("Failed to create a pipe to call dbus-launch: %s\n", + _dbus_strerror (errno)); + goto out; + } + + pid = fork (); + if (pid < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to fork(): %s", + _dbus_strerror (errno)); + _dbus_verbose ("Failed to fork() to call dbus-launch: %s\n", + _dbus_strerror (errno)); + goto out; + } + + if (pid == 0) + { + /* child process */ + int maxfds; + int fd; + + fd = open ("/dev/null", O_RDWR); + if (fd == -1) + /* huh?! can't open /dev/null? */ + _exit (1); + + _dbus_verbose ("/dev/null fd %d opened\n", fd); + + /* set-up stdXXX */ + close (address_pipe[READ_END]); + close (errors_pipe[READ_END]); + close (0); /* close stdin */ + close (1); /* close stdout */ + close (2); /* close stderr */ + + if (dup2 (fd, 0) == -1) + _exit (1); + if (dup2 (address_pipe[WRITE_END], 1) == -1) + _exit (1); + if (dup2 (errors_pipe[WRITE_END], 2) == -1) + _exit (1); + + maxfds = sysconf (_SC_OPEN_MAX); + /* Pick something reasonable if for some reason sysconf + * says unlimited. + */ + if (maxfds < 0) + maxfds = 1024; + /* close all inherited fds */ + for (i = 3; i < maxfds; i++) + close (i); + + execv (DBUS_BINDIR "/dbus-launch", argv); + + /* failed, try searching PATH */ + execvp ("dbus-launch", argv); + + /* still nothing, we failed */ + _exit (1); + } + + /* parent process */ + close (address_pipe[WRITE_END]); + close (errors_pipe[WRITE_END]); + address_pipe[WRITE_END] = -1; + errors_pipe[WRITE_END] = -1; + + ret = 0; + do + { + ret = _dbus_read (address_pipe[READ_END], address, 1024); + } + while (ret > 0); + + /* reap the child process to avoid it lingering as zombie */ + do + { + ret = waitpid (pid, &status, 0); + } + while (ret == -1 && errno == EINTR); + + /* We succeeded if the process exited with status 0 and + anything was read */ + if (!WIFEXITED (status) || WEXITSTATUS (status) != 0 || + _dbus_string_get_length (address) == orig_len) + { + /* The process ended with error */ + DBusString error_message; + _dbus_string_init (&error_message); + ret = 0; + do + { + ret = _dbus_read (errors_pipe[READ_END], &error_message, 1024); + } + while (ret > 0); + + _dbus_string_set_length (address, orig_len); + if (_dbus_string_get_length (&error_message) > 0) + dbus_set_error (error, DBUS_ERROR_SPAWN_EXEC_FAILED, + "dbus-launch failed to autolaunch D-Bus session: %s", + _dbus_string_get_data (&error_message)); + else + dbus_set_error (error, DBUS_ERROR_SPAWN_EXEC_FAILED, + "Failed to execute dbus-launch to autolaunch D-Bus session"); + goto out; + } + + retval = TRUE; + + out: + if (retval) + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + else + _DBUS_ASSERT_ERROR_IS_SET (error); + + if (address_pipe[0] != -1) + close (address_pipe[0]); + if (address_pipe[1] != -1) + close (address_pipe[1]); + if (errors_pipe[0] != -1) + close (errors_pipe[0]); + if (errors_pipe[1] != -1) + close (errors_pipe[1]); + + _dbus_string_free (&uuid); + return retval; +} + +/** + * Reads the uuid of the machine we're running on from + * the dbus configuration. Optionally try to create it + * (only root can do this usually). + * + * On UNIX, reads a file that gets created by dbus-uuidgen + * in a post-install script. On Windows, if there's a standard + * machine uuid we could just use that, but I can't find one + * with the right properties (the hardware profile guid can change + * without rebooting I believe). If there's no standard one + * we might want to use the registry instead of a file for + * this, and I'm not sure how we'd ensure the uuid gets created. + * + * @param machine_id guid to init with the machine's uuid + * @param create_if_not_found try to create the uuid if it doesn't exist + * @param error the error return + * @returns #FALSE if the error is set + */ +dbus_bool_t +_dbus_read_local_machine_uuid (DBusGUID *machine_id, + dbus_bool_t create_if_not_found, + DBusError *error) +{ + DBusString filename; + _dbus_string_init_const (&filename, DBUS_MACHINE_UUID_FILE); + return _dbus_read_uuid_file (&filename, machine_id, create_if_not_found, error); +} + +#define DBUS_UNIX_STANDARD_SESSION_SERVICEDIR "/dbus-1/services" +#define DBUS_UNIX_STANDARD_SYSTEM_SERVICEDIR "/dbus-1/system-services" + + +/** + * Returns the standard directories for a session bus to look for service + * activation files + * + * On UNIX this should be the standard xdg freedesktop.org data directories: + * + * XDG_DATA_HOME=${XDG_DATA_HOME-$HOME/.local/share} + * XDG_DATA_DIRS=${XDG_DATA_DIRS-/usr/local/share:/usr/share} + * + * and + * + * DBUS_DATADIR + * + * @param dirs the directory list we are returning + * @returns #FALSE on OOM + */ + +dbus_bool_t +_dbus_get_standard_session_servicedirs (DBusList **dirs) +{ + const char *xdg_data_home; + const char *xdg_data_dirs; + DBusString servicedir_path; + + if (!_dbus_string_init (&servicedir_path)) + return FALSE; + + xdg_data_home = _dbus_getenv ("XDG_DATA_HOME"); + xdg_data_dirs = _dbus_getenv ("XDG_DATA_DIRS"); + + if (xdg_data_dirs != NULL) + { + if (!_dbus_string_append (&servicedir_path, xdg_data_dirs)) + goto oom; + + if (!_dbus_string_append (&servicedir_path, ":")) + goto oom; + } + else + { + if (!_dbus_string_append (&servicedir_path, "/usr/local/share:/usr/share:")) + goto oom; + } + + /* + * add configured datadir to defaults + * this may be the same as an xdg dir + * however the config parser should take + * care of duplicates + */ + if (!_dbus_string_append (&servicedir_path, DBUS_DATADIR":")) + goto oom; + + if (xdg_data_home != NULL) + { + if (!_dbus_string_append (&servicedir_path, xdg_data_home)) + goto oom; + } + else + { + const DBusString *homedir; + DBusString local_share; + + if (!_dbus_homedir_from_current_process (&homedir)) + goto oom; + + if (!_dbus_string_append (&servicedir_path, _dbus_string_get_const_data (homedir))) + goto oom; + + _dbus_string_init_const (&local_share, "/.local/share"); + if (!_dbus_concat_dir_and_file (&servicedir_path, &local_share)) + goto oom; + } + + if (!_dbus_split_paths_and_append (&servicedir_path, + DBUS_UNIX_STANDARD_SESSION_SERVICEDIR, + dirs)) + goto oom; + + _dbus_string_free (&servicedir_path); + return TRUE; + + oom: + _dbus_string_free (&servicedir_path); + return FALSE; +} + + +/** + * Returns the standard directories for a system bus to look for service + * activation files + * + * On UNIX this should be the standard xdg freedesktop.org data directories: + * + * XDG_DATA_DIRS=${XDG_DATA_DIRS-/usr/local/share:/usr/share} + * + * and + * + * DBUS_DATADIR + * + * On Windows there is no system bus and this function can return nothing. + * + * @param dirs the directory list we are returning + * @returns #FALSE on OOM + */ + +dbus_bool_t +_dbus_get_standard_system_servicedirs (DBusList **dirs) +{ + const char *xdg_data_dirs; + DBusString servicedir_path; + + if (!_dbus_string_init (&servicedir_path)) + return FALSE; + + xdg_data_dirs = _dbus_getenv ("XDG_DATA_DIRS"); + + if (xdg_data_dirs != NULL) + { + if (!_dbus_string_append (&servicedir_path, xdg_data_dirs)) + goto oom; + + if (!_dbus_string_append (&servicedir_path, ":")) + goto oom; + } + else + { + if (!_dbus_string_append (&servicedir_path, "/usr/local/share:/usr/share:")) + goto oom; + } + + /* + * add configured datadir to defaults + * this may be the same as an xdg dir + * however the config parser should take + * care of duplicates + */ + if (!_dbus_string_append (&servicedir_path, DBUS_DATADIR":")) + goto oom; + + if (!_dbus_split_paths_and_append (&servicedir_path, + DBUS_UNIX_STANDARD_SYSTEM_SERVICEDIR, + dirs)) + goto oom; + + _dbus_string_free (&servicedir_path); + return TRUE; + + oom: + _dbus_string_free (&servicedir_path); + return FALSE; +} + +/** + * Append the absolute path of the system.conf file + * (there is no system bus on Windows so this can just + * return FALSE and print a warning or something) + * + * @param str the string to append to + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_append_system_config_file (DBusString *str) +{ + return _dbus_string_append (str, DBUS_SYSTEM_CONFIG_FILE); +} + +/** + * Append the absolute path of the session.conf file. + * + * @param str the string to append to + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_append_session_config_file (DBusString *str) +{ + return _dbus_string_append (str, DBUS_SESSION_CONFIG_FILE); +} + +/** + * Called when the bus daemon is signaled to reload its configuration; any + * caches should be nuked. Of course any caches that need explicit reload + * are probably broken, but c'est la vie. + * + * + */ +void +_dbus_flush_caches (void) +{ + _dbus_user_database_flush_system (); +} + +/** + * Appends the directory in which a keyring for the given credentials + * should be stored. The credentials should have either a Windows or + * UNIX user in them. The directory should be an absolute path. + * + * On UNIX the directory is ~/.dbus-keyrings while on Windows it should probably + * be something else, since the dotfile convention is not normal on Windows. + * + * @param directory string to append directory to + * @param credentials credentials the directory should be for + * + * @returns #FALSE on no memory + */ +dbus_bool_t +_dbus_append_keyring_directory_for_credentials (DBusString *directory, + DBusCredentials *credentials) +{ + DBusString homedir; + DBusString dotdir; + dbus_uid_t uid; + + _dbus_assert (credentials != NULL); + _dbus_assert (!_dbus_credentials_are_anonymous (credentials)); + + if (!_dbus_string_init (&homedir)) + return FALSE; + + uid = _dbus_credentials_get_unix_uid (credentials); + _dbus_assert (uid != DBUS_UID_UNSET); + + if (!_dbus_homedir_from_uid (uid, &homedir)) + goto failed; + +#ifdef DBUS_BUILD_TESTS + { + const char *override; + + override = _dbus_getenv ("DBUS_TEST_HOMEDIR"); + if (override != NULL && *override != '\0') + { + _dbus_string_set_length (&homedir, 0); + if (!_dbus_string_append (&homedir, override)) + goto failed; + + _dbus_verbose ("Using fake homedir for testing: %s\n", + _dbus_string_get_const_data (&homedir)); + } + else + { + static dbus_bool_t already_warned = FALSE; + if (!already_warned) + { + _dbus_warn ("Using your real home directory for testing, set DBUS_TEST_HOMEDIR to avoid\n"); + already_warned = TRUE; + } + } + } +#endif + + _dbus_string_init_const (&dotdir, ".dbus-keyrings"); + if (!_dbus_concat_dir_and_file (&homedir, + &dotdir)) + goto failed; + + if (!_dbus_string_copy (&homedir, 0, + directory, _dbus_string_get_length (directory))) { + goto failed; + } + + _dbus_string_free (&homedir); + return TRUE; + + failed: + _dbus_string_free (&homedir); + return FALSE; +} + + +/** + * See if errno is EAGAIN or EWOULDBLOCK (this has to be done differently + * for Winsock so is abstracted) + * + * @returns #TRUE if errno == EAGAIN or errno == EWOULDBLOCK + */ +dbus_bool_t +_dbus_get_is_errno_eagain_or_ewouldblock (void) +{ + return errno == EAGAIN || errno == EWOULDBLOCK; +} + +/* tests in dbus-sysdeps-util.c */ diff --git a/src/dbus/dbus-sysdeps-unix.h b/src/dbus/dbus-sysdeps-unix.h new file mode 100644 index 0000000..0005cd8 --- /dev/null +++ b/src/dbus/dbus-sysdeps-unix.h @@ -0,0 +1,134 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-sysdeps-unix.h UNIX-specific wrappers around system/libc features (internal to D-Bus implementation) + * + * Copyright (C) 2002, 2003, 2006 Red Hat, Inc. + * Copyright (C) 2003 CodeFactory AB + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef DBUS_SYSDEPS_UNIX_H +#define DBUS_SYSDEPS_UNIX_H + +#include +#include + +#ifdef DBUS_WIN +#error "Don't include this on Windows" +#endif + +DBUS_BEGIN_DECLS + +/** + * @defgroup DBusSysdepsUnix UNIX-specific internal API + * @ingroup DBusInternals + * @brief Internal system-dependent API available on UNIX only + * @{ + */ + +dbus_bool_t +_dbus_close (int fd, + DBusError *error); +int +_dbus_read (int fd, + DBusString *buffer, + int count); +int +_dbus_write (int fd, + const DBusString *buffer, + int start, + int len); +int +_dbus_write_two (int fd, + const DBusString *buffer1, + int start1, + int len1, + const DBusString *buffer2, + int start2, + int len2); + +dbus_bool_t _dbus_open_unix_socket (int *fd, + DBusError *error); +int _dbus_connect_unix_socket (const char *path, + dbus_bool_t abstract, + DBusError *error); +int _dbus_listen_unix_socket (const char *path, + dbus_bool_t abstract, + DBusError *error); + +dbus_bool_t _dbus_read_credentials (int client_fd, + DBusCredentials *credentials, + DBusError *error); +dbus_bool_t _dbus_send_credentials (int server_fd, + DBusError *error); + +/** Information about a UNIX user */ +typedef struct DBusUserInfo DBusUserInfo; +/** Information about a UNIX group */ +typedef struct DBusGroupInfo DBusGroupInfo; + +/** + * Information about a UNIX user + */ +struct DBusUserInfo +{ + dbus_uid_t uid; /**< UID */ + dbus_gid_t primary_gid; /**< GID */ + dbus_gid_t *group_ids; /**< Groups IDs, *including* above primary group */ + int n_group_ids; /**< Size of group IDs array */ + char *username; /**< Username */ + char *homedir; /**< Home directory */ +}; + +/** + * Information about a UNIX group + */ +struct DBusGroupInfo +{ + dbus_gid_t gid; /**< GID */ + char *groupname; /**< Group name */ +}; + +dbus_bool_t _dbus_user_info_fill (DBusUserInfo *info, + const DBusString *username, + DBusError *error); +dbus_bool_t _dbus_user_info_fill_uid (DBusUserInfo *info, + dbus_uid_t uid, + DBusError *error); +void _dbus_user_info_free (DBusUserInfo *info); + +dbus_bool_t _dbus_group_info_fill (DBusGroupInfo *info, + const DBusString *groupname, + DBusError *error); +dbus_bool_t _dbus_group_info_fill_gid (DBusGroupInfo *info, + dbus_gid_t gid, + DBusError *error); +void _dbus_group_info_free (DBusGroupInfo *info); + +dbus_uid_t _dbus_getuid (void); +dbus_uid_t _dbus_geteuid (void); +dbus_gid_t _dbus_getgid (void); + +dbus_bool_t _dbus_parse_uid (const DBusString *uid_str, + dbus_uid_t *uid); + +/** @} */ + +DBUS_END_DECLS + +#endif /* DBUS_SYSDEPS_UNIX_H */ diff --git a/src/dbus/dbus-sysdeps-util-unix.c b/src/dbus/dbus-sysdeps-util-unix.c new file mode 100644 index 0000000..d31e144 --- /dev/null +++ b/src/dbus/dbus-sysdeps-util-unix.c @@ -0,0 +1,1237 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-sysdeps-util-unix.c Would be in dbus-sysdeps-unix.c, but not used in libdbus + * + * Copyright (C) 2002, 2003, 2004, 2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include "dbus-sysdeps.h" +#include "dbus-sysdeps-unix.h" +#include "dbus-internals.h" +#include "dbus-protocol.h" +#include "dbus-string.h" +#define DBUS_USERDB_INCLUDES_PRIVATE 1 +#include "dbus-userdb.h" +#include "dbus-test.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LIBAUDIT +#include +#include +#include +#endif /* HAVE_LIBAUDIT */ + +#ifdef HAVE_SYS_SYSLIMITS_H +#include +#endif + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +/** + * @addtogroup DBusInternalsUtils + * @{ + */ + + +/** + * Does the chdir, fork, setsid, etc. to become a daemon process. + * + * @param pidfile #NULL, or pidfile to create + * @param print_pid_pipe pipe to print daemon's pid to, or -1 for none + * @param error return location for errors + * @param keep_umask #TRUE to keep the original umask + * @returns #FALSE on failure + */ +dbus_bool_t +_dbus_become_daemon (const DBusString *pidfile, + DBusPipe *print_pid_pipe, + DBusError *error, + dbus_bool_t keep_umask) +{ + const char *s; + pid_t child_pid; + int dev_null_fd; + + _dbus_verbose ("Becoming a daemon...\n"); + + _dbus_verbose ("chdir to /\n"); + if (chdir ("/") < 0) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Could not chdir() to root directory"); + return FALSE; + } + + _dbus_verbose ("forking...\n"); + switch ((child_pid = fork ())) + { + case -1: + _dbus_verbose ("fork failed\n"); + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to fork daemon: %s", _dbus_strerror (errno)); + return FALSE; + break; + + case 0: + _dbus_verbose ("in child, closing std file descriptors\n"); + + /* silently ignore failures here, if someone + * doesn't have /dev/null we may as well try + * to continue anyhow + */ + + dev_null_fd = open ("/dev/null", O_RDWR); + if (dev_null_fd >= 0) + { + dup2 (dev_null_fd, 0); + dup2 (dev_null_fd, 1); + + s = _dbus_getenv ("DBUS_DEBUG_OUTPUT"); + if (s == NULL || *s == '\0') + dup2 (dev_null_fd, 2); + else + _dbus_verbose ("keeping stderr open due to DBUS_DEBUG_OUTPUT\n"); + } + + if (!keep_umask) + { + /* Get a predictable umask */ + _dbus_verbose ("setting umask\n"); + umask (022); + } + + _dbus_verbose ("calling setsid()\n"); + if (setsid () == -1) + _dbus_assert_not_reached ("setsid() failed"); + + break; + + default: + if (!_dbus_write_pid_to_file_and_pipe (pidfile, print_pid_pipe, + child_pid, error)) + { + _dbus_verbose ("pid file or pipe write failed: %s\n", + error->message); + kill (child_pid, SIGTERM); + return FALSE; + } + + _dbus_verbose ("parent exiting\n"); + _exit (0); + break; + } + + return TRUE; +} + + +/** + * Creates a file containing the process ID. + * + * @param filename the filename to write to + * @param pid our process ID + * @param error return location for errors + * @returns #FALSE on failure + */ +static dbus_bool_t +_dbus_write_pid_file (const DBusString *filename, + unsigned long pid, + DBusError *error) +{ + const char *cfilename; + int fd; + FILE *f; + + cfilename = _dbus_string_get_const_data (filename); + + fd = open (cfilename, O_WRONLY|O_CREAT|O_EXCL|O_BINARY, 0644); + + if (fd < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to open \"%s\": %s", cfilename, + _dbus_strerror (errno)); + return FALSE; + } + + if ((f = fdopen (fd, "w")) == NULL) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to fdopen fd %d: %s", fd, _dbus_strerror (errno)); + _dbus_close (fd, NULL); + return FALSE; + } + + if (fprintf (f, "%lu\n", pid) < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to write to \"%s\": %s", cfilename, + _dbus_strerror (errno)); + + fclose (f); + return FALSE; + } + + if (fclose (f) == EOF) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to close \"%s\": %s", cfilename, + _dbus_strerror (errno)); + return FALSE; + } + + return TRUE; +} + +/** + * Writes the given pid_to_write to a pidfile (if non-NULL) and/or to a + * pipe (if non-NULL). Does nothing if pidfile and print_pid_pipe are both + * NULL. + * + * @param pidfile the file to write to or #NULL + * @param print_pid_pipe the pipe to write to or #NULL + * @param pid_to_write the pid to write out + * @param error error on failure + * @returns FALSE if error is set + */ +dbus_bool_t +_dbus_write_pid_to_file_and_pipe (const DBusString *pidfile, + DBusPipe *print_pid_pipe, + dbus_pid_t pid_to_write, + DBusError *error) +{ + if (pidfile) + { + _dbus_verbose ("writing pid file %s\n", _dbus_string_get_const_data (pidfile)); + if (!_dbus_write_pid_file (pidfile, + pid_to_write, + error)) + { + _dbus_verbose ("pid file write failed\n"); + _DBUS_ASSERT_ERROR_IS_SET(error); + return FALSE; + } + } + else + { + _dbus_verbose ("No pid file requested\n"); + } + + if (print_pid_pipe != NULL && _dbus_pipe_is_valid (print_pid_pipe)) + { + DBusString pid; + int bytes; + + _dbus_verbose ("writing our pid to pipe %d\n", print_pid_pipe->fd_or_handle); + + if (!_dbus_string_init (&pid)) + { + _DBUS_SET_OOM (error); + return FALSE; + } + + if (!_dbus_string_append_int (&pid, pid_to_write) || + !_dbus_string_append (&pid, "\n")) + { + _dbus_string_free (&pid); + _DBUS_SET_OOM (error); + return FALSE; + } + + bytes = _dbus_string_get_length (&pid); + if (_dbus_pipe_write (print_pid_pipe, &pid, 0, bytes, error) != bytes) + { + /* _dbus_pipe_write sets error only on failure, not short write */ + if (error != NULL && !dbus_error_is_set(error)) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Printing message bus PID: did not write enough bytes\n"); + } + _dbus_string_free (&pid); + return FALSE; + } + + _dbus_string_free (&pid); + } + else + { + _dbus_verbose ("No pid pipe to write to\n"); + } + + return TRUE; +} + +/** + * Verify that after the fork we can successfully change to this user. + * + * @param user the username given in the daemon configuration + * @returns #TRUE if username is valid + */ +dbus_bool_t +_dbus_verify_daemon_user (const char *user) +{ + DBusString u; + + _dbus_string_init_const (&u, user); + + return _dbus_get_user_id_and_primary_group (&u, NULL, NULL); +} + +/** + * 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; +#ifdef HAVE_LIBAUDIT + dbus_bool_t we_were_root; + cap_t new_caps; +#endif + + _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; + } + +#ifdef HAVE_LIBAUDIT + we_were_root = _dbus_geteuid () == 0; + new_caps = NULL; + /* have a tmp set of caps that we use to transition to the usr/grp dbus should + * run as ... doesn't really help. But keeps people happy. + */ + + if (we_were_root) + { + cap_value_t new_cap_list[] = { CAP_AUDIT_WRITE }; + cap_value_t tmp_cap_list[] = { CAP_AUDIT_WRITE, CAP_SETUID, CAP_SETGID }; + cap_t tmp_caps = cap_init(); + + if (!tmp_caps || !(new_caps = cap_init ())) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Failed to initialize drop of capabilities: %s\n", + _dbus_strerror (errno)); + + if (tmp_caps) + cap_free (tmp_caps); + + return FALSE; + } + + /* assume these work... */ + cap_set_flag (new_caps, CAP_PERMITTED, 1, new_cap_list, CAP_SET); + cap_set_flag (new_caps, CAP_EFFECTIVE, 1, new_cap_list, CAP_SET); + cap_set_flag (tmp_caps, CAP_PERMITTED, 3, tmp_cap_list, CAP_SET); + cap_set_flag (tmp_caps, CAP_EFFECTIVE, 3, tmp_cap_list, CAP_SET); + + if (prctl (PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to set keep-capabilities: %s\n", + _dbus_strerror (errno)); + cap_free (tmp_caps); + goto fail; + } + + if (cap_set_proc (tmp_caps) == -1) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Failed to drop capabilities: %s\n", + _dbus_strerror (errno)); + cap_free (tmp_caps); + goto fail; + } + cap_free (tmp_caps); + } +#endif /* HAVE_LIBAUDIT */ + + /* setgroups() only works if we are a privileged process, + * so we don't return error on failure; the only possible + * failure is that we don't have perms to do it. + * + * not sure this is right, maybe if setuid() + * is going to work then setgroups() should also work. + */ + if (setgroups (0, NULL) < 0) + _dbus_warn ("Failed to drop supplementary groups: %s\n", + _dbus_strerror (errno)); + + /* Set GID first, or the setuid may remove our permission + * to change the GID + */ + if (setgid (gid) < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to set GID to %lu: %s", gid, + _dbus_strerror (errno)); + goto fail; + } + + if (setuid (uid) < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to set UID to %lu: %s", uid, + _dbus_strerror (errno)); + goto fail; + } + +#ifdef HAVE_LIBAUDIT + if (we_were_root) + { + if (cap_set_proc (new_caps)) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Failed to drop capabilities: %s\n", + _dbus_strerror (errno)); + goto fail; + } + cap_free (new_caps); + + /* should always work, if it did above */ + if (prctl (PR_SET_KEEPCAPS, 0, 0, 0, 0) == -1) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to unset keep-capabilities: %s\n", + _dbus_strerror (errno)); + return FALSE; + } + } +#endif + + return TRUE; + + fail: +#ifdef HAVE_LIBAUDIT + if (!we_were_root) + { + /* should always work, if it did above */ + prctl (PR_SET_KEEPCAPS, 0, 0, 0, 0); + cap_free (new_caps); + } +#endif + + return FALSE; +} + +void +_dbus_init_system_log (void) +{ + openlog ("dbus", LOG_PID, LOG_DAEMON); +} + +/** + * Log an informative message. Intended for use primarily by + * the system bus. + * + * @param msg a printf-style format string + * @param args arguments for the format string + */ +void +_dbus_log_info (const char *msg, va_list args) +{ + vsyslog (LOG_DAEMON|LOG_NOTICE, msg, args); +} + +/** + * Log a security-related message. Intended for use primarily by + * the system bus. + * + * @param msg a printf-style format string + * @param args arguments for the format string + */ +void +_dbus_log_security (const char *msg, va_list args) +{ + vsyslog (LOG_AUTH|LOG_NOTICE, msg, args); +} + +/** Installs a UNIX signal handler + * + * @param sig the signal to handle + * @param handler the handler + */ +void +_dbus_set_signal_handler (int sig, + DBusSignalHandler handler) +{ + struct sigaction act; + sigset_t empty_mask; + + sigemptyset (&empty_mask); + act.sa_handler = handler; + act.sa_mask = empty_mask; + act.sa_flags = 0; + sigaction (sig, &act, NULL); +} + + +/** + * Removes a directory; Directory must be empty + * + * @param filename directory filename + * @param error initialized error object + * @returns #TRUE on success + */ +dbus_bool_t +_dbus_delete_directory (const DBusString *filename, + DBusError *error) +{ + const char *filename_c; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + filename_c = _dbus_string_get_const_data (filename); + + if (rmdir (filename_c) != 0) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Failed to remove directory %s: %s\n", + filename_c, _dbus_strerror (errno)); + return FALSE; + } + + return TRUE; +} + +/** Checks if a file exists +* +* @param file full path to the file +* @returns #TRUE if file exists +*/ +dbus_bool_t +_dbus_file_exists (const char *file) +{ + return (access (file, F_OK) == 0); +} + +/** Checks if user is at the console +* +* @param username user to check +* @param error return location for errors +* @returns #TRUE is the user is at the consolei and there are no errors +*/ +dbus_bool_t +_dbus_user_at_console (const char *username, + DBusError *error) +{ + + DBusString f; + dbus_bool_t result; + + result = FALSE; + if (!_dbus_string_init (&f)) + { + _DBUS_SET_OOM (error); + return FALSE; + } + + if (!_dbus_string_append (&f, DBUS_CONSOLE_AUTH_DIR)) + { + _DBUS_SET_OOM (error); + goto out; + } + + + if (!_dbus_string_append (&f, username)) + { + _DBUS_SET_OOM (error); + goto out; + } + + result = _dbus_file_exists (_dbus_string_get_const_data (&f)); + + out: + _dbus_string_free (&f); + + return result; +} + + +/** + * Checks whether the filename is an absolute path + * + * @param filename the filename + * @returns #TRUE if an absolute path + */ +dbus_bool_t +_dbus_path_is_absolute (const DBusString *filename) +{ + if (_dbus_string_get_length (filename) > 0) + return _dbus_string_get_byte (filename, 0) == '/'; + else + return FALSE; +} + +/** + * stat() wrapper. + * + * @param filename the filename to stat + * @param statbuf the stat info to fill in + * @param error return location for error + * @returns #FALSE if error was set + */ +dbus_bool_t +_dbus_stat (const DBusString *filename, + DBusStat *statbuf, + DBusError *error) +{ + const char *filename_c; + struct stat sb; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + filename_c = _dbus_string_get_const_data (filename); + + if (stat (filename_c, &sb) < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "%s", _dbus_strerror (errno)); + return FALSE; + } + + statbuf->mode = sb.st_mode; + statbuf->nlink = sb.st_nlink; + statbuf->uid = sb.st_uid; + statbuf->gid = sb.st_gid; + statbuf->size = sb.st_size; + statbuf->atime = sb.st_atime; + statbuf->mtime = sb.st_mtime; + statbuf->ctime = sb.st_ctime; + + return TRUE; +} + + +/** + * Internals of directory iterator + */ +struct DBusDirIter +{ + DIR *d; /**< The DIR* from opendir() */ + +}; + +/** + * Open a directory to iterate over. + * + * @param filename the directory name + * @param error exception return object or #NULL + * @returns new iterator, or #NULL on error + */ +DBusDirIter* +_dbus_directory_open (const DBusString *filename, + DBusError *error) +{ + DIR *d; + DBusDirIter *iter; + const char *filename_c; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + filename_c = _dbus_string_get_const_data (filename); + + d = opendir (filename_c); + if (d == NULL) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to read directory \"%s\": %s", + filename_c, + _dbus_strerror (errno)); + return NULL; + } + iter = dbus_new0 (DBusDirIter, 1); + if (iter == NULL) + { + closedir (d); + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, + "Could not allocate memory for directory iterator"); + return NULL; + } + + iter->d = d; + + return iter; +} + +/* Calculate the required buffer size (in bytes) for directory + * entries read from the given directory handle. Return -1 if this + * this cannot be done. + * + * If you use autoconf, include fpathconf and dirfd in your + * AC_CHECK_FUNCS list. Otherwise use some other method to detect + * and use them where available. + */ +static dbus_bool_t +dirent_buf_size(DIR * dirp, size_t *size) +{ + long name_max; +# if defined(HAVE_FPATHCONF) && defined(_PC_NAME_MAX) +# if defined(HAVE_DIRFD) + name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX); +# elif defined(HAVE_DDFD) + name_max = fpathconf(dirp->dd_fd, _PC_NAME_MAX); +# else + name_max = fpathconf(dirp->__dd_fd, _PC_NAME_MAX); +# endif /* HAVE_DIRFD */ + if (name_max == -1) +# if defined(NAME_MAX) + name_max = NAME_MAX; +# else + return FALSE; +# endif +# elif defined(MAXNAMELEN) + name_max = MAXNAMELEN; +# else +# if defined(NAME_MAX) + name_max = NAME_MAX; +# else +# error "buffer size for readdir_r cannot be determined" +# endif +# endif + if (size) + *size = (size_t)offsetof(struct dirent, d_name) + name_max + 1; + else + return FALSE; + + return TRUE; +} + +/** + * Get next file in the directory. Will not return "." or ".." on + * UNIX. If an error occurs, the contents of "filename" are + * undefined. The error is never set if the function succeeds. + * + * @param iter the iterator + * @param filename string to be set to the next file in the dir + * @param error return location for error + * @returns #TRUE if filename was filled in with a new filename + */ +dbus_bool_t +_dbus_directory_get_next_file (DBusDirIter *iter, + DBusString *filename, + DBusError *error) +{ + struct dirent *d, *ent; + size_t buf_size; + int err; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + if (!dirent_buf_size (iter->d, &buf_size)) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Can't calculate buffer size when reading directory"); + return FALSE; + } + + d = (struct dirent *)dbus_malloc (buf_size); + if (!d) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, + "No memory to read directory entry"); + return FALSE; + } + + again: + err = readdir_r (iter->d, d, &ent); + if (err || !ent) + { + if (err != 0) + dbus_set_error (error, + _dbus_error_from_errno (err), + "%s", _dbus_strerror (err)); + + dbus_free (d); + return FALSE; + } + else if (ent->d_name[0] == '.' && + (ent->d_name[1] == '\0' || + (ent->d_name[1] == '.' && ent->d_name[2] == '\0'))) + goto again; + else + { + _dbus_string_set_length (filename, 0); + if (!_dbus_string_append (filename, ent->d_name)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, + "No memory to read directory entry"); + dbus_free (d); + return FALSE; + } + else + { + dbus_free (d); + return TRUE; + } + } +} + +/** + * Closes a directory iteration. + */ +void +_dbus_directory_close (DBusDirIter *iter) +{ + closedir (iter->d); + dbus_free (iter); +} + +static dbus_bool_t +fill_user_info_from_group (struct group *g, + DBusGroupInfo *info, + DBusError *error) +{ + _dbus_assert (g->gr_name != NULL); + + info->gid = g->gr_gid; + info->groupname = _dbus_strdup (g->gr_name); + + /* info->members = dbus_strdupv (g->gr_mem) */ + + if (info->groupname == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return FALSE; + } + + return TRUE; +} + +static dbus_bool_t +fill_group_info (DBusGroupInfo *info, + dbus_gid_t gid, + const DBusString *groupname, + DBusError *error) +{ + const char *group_c_str; + + _dbus_assert (groupname != NULL || gid != DBUS_GID_UNSET); + _dbus_assert (groupname == NULL || gid == DBUS_GID_UNSET); + + if (groupname) + group_c_str = _dbus_string_get_const_data (groupname); + else + group_c_str = NULL; + + /* For now assuming that the getgrnam() and getgrgid() flavors + * always correspond to the pwnam flavors, if not we have + * to add more configure checks. + */ + +#if defined (HAVE_POSIX_GETPWNAM_R) || defined (HAVE_NONPOSIX_GETPWNAM_R) + { + struct group *g; + int result; + size_t buflen; + char *buf; + struct group g_str; + dbus_bool_t b; + + /* retrieve maximum needed size for buf */ + buflen = sysconf (_SC_GETGR_R_SIZE_MAX); + + /* sysconf actually returns a long, but everything else expects size_t, + * so just recast here. + * https://bugs.freedesktop.org/show_bug.cgi?id=17061 + */ + if ((long) buflen <= 0) + buflen = 1024; + + result = -1; + while (1) + { + buf = dbus_malloc (buflen); + if (buf == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return FALSE; + } + + g = NULL; +#ifdef HAVE_POSIX_GETPWNAM_R + if (group_c_str) + result = getgrnam_r (group_c_str, &g_str, buf, buflen, + &g); + else + result = getgrgid_r (gid, &g_str, buf, buflen, + &g); +#else + g = getgrnam_r (group_c_str, &g_str, buf, buflen); + result = 0; +#endif /* !HAVE_POSIX_GETPWNAM_R */ + /* Try a bigger buffer if ERANGE was returned: + https://bugs.freedesktop.org/show_bug.cgi?id=16727 + */ + if (result == ERANGE && buflen < 512 * 1024) + { + dbus_free (buf); + buflen *= 2; + } + else + { + break; + } + } + + if (result == 0 && g == &g_str) + { + b = fill_user_info_from_group (g, info, error); + dbus_free (buf); + return b; + } + else + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Group %s unknown or failed to look it up\n", + group_c_str ? group_c_str : "???"); + dbus_free (buf); + return FALSE; + } + } +#else /* ! HAVE_GETPWNAM_R */ + { + /* I guess we're screwed on thread safety here */ + struct group *g; + + g = getgrnam (group_c_str); + + if (g != NULL) + { + return fill_user_info_from_group (g, info, error); + } + else + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Group %s unknown or failed to look it up\n", + group_c_str ? group_c_str : "???"); + return FALSE; + } + } +#endif /* ! HAVE_GETPWNAM_R */ +} + +/** + * Initializes the given DBusGroupInfo struct + * with information about the given group name. + * + * @param info the group info struct + * @param groupname name of group + * @param error the error return + * @returns #FALSE if error is set + */ +dbus_bool_t +_dbus_group_info_fill (DBusGroupInfo *info, + const DBusString *groupname, + DBusError *error) +{ + return fill_group_info (info, DBUS_GID_UNSET, + groupname, error); + +} + +/** + * Initializes the given DBusGroupInfo struct + * with information about the given group ID. + * + * @param info the group info struct + * @param gid group ID + * @param error the error return + * @returns #FALSE if error is set + */ +dbus_bool_t +_dbus_group_info_fill_gid (DBusGroupInfo *info, + dbus_gid_t gid, + DBusError *error) +{ + return fill_group_info (info, gid, NULL, error); +} + +/** + * Parse a UNIX user from the bus config file. On Windows, this should + * simply always fail (just return #FALSE). + * + * @param username the username text + * @param uid_p place to return the uid + * @returns #TRUE on success + */ +dbus_bool_t +_dbus_parse_unix_user_from_config (const DBusString *username, + dbus_uid_t *uid_p) +{ + return _dbus_get_user_id (username, uid_p); + +} + +/** + * Parse a UNIX group from the bus config file. On Windows, this should + * simply always fail (just return #FALSE). + * + * @param groupname the groupname text + * @param gid_p place to return the gid + * @returns #TRUE on success + */ +dbus_bool_t +_dbus_parse_unix_group_from_config (const DBusString *groupname, + dbus_gid_t *gid_p) +{ + return _dbus_get_group_id (groupname, gid_p); +} + +/** + * Gets all groups corresponding to the given UNIX user ID. On UNIX, + * just calls _dbus_groups_from_uid(). On Windows, should always + * fail since we don't know any UNIX groups. + * + * @param uid the UID + * @param group_ids return location for array of group IDs + * @param n_group_ids return location for length of returned array + * @returns #TRUE if the UID existed and we got some credentials + */ +dbus_bool_t +_dbus_unix_groups_from_uid (dbus_uid_t uid, + dbus_gid_t **group_ids, + int *n_group_ids) +{ + return _dbus_groups_from_uid (uid, group_ids, n_group_ids); +} + +/** + * Checks to see if the UNIX user ID is at the console. + * Should always fail on Windows (set the error to + * #DBUS_ERROR_NOT_SUPPORTED). + * + * @param uid UID of person to check + * @param error return location for errors + * @returns #TRUE if the UID is the same as the console user and there are no errors + */ +dbus_bool_t +_dbus_unix_user_is_at_console (dbus_uid_t uid, + DBusError *error) +{ + return _dbus_is_console_user (uid, error); + +} + +/** + * Checks to see if the UNIX user ID matches the UID of + * the process. Should always return #FALSE on Windows. + * + * @param uid the UNIX user ID + * @returns #TRUE if this uid owns the process. + */ +dbus_bool_t +_dbus_unix_user_is_process_owner (dbus_uid_t uid) +{ + return uid == _dbus_geteuid (); +} + +/** + * Checks to see if the Windows user SID matches the owner of + * the process. Should always return #FALSE on UNIX. + * + * @param windows_sid the Windows user SID + * @returns #TRUE if this user owns the process. + */ +dbus_bool_t +_dbus_windows_user_is_process_owner (const char *windows_sid) +{ + return FALSE; +} + +/** @} */ /* End of DBusInternalsUtils functions */ + +/** + * @addtogroup DBusString + * + * @{ + */ +/** + * Get the directory name from a complete filename + * @param filename the filename + * @param dirname string to append directory name to + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_string_get_dirname (const DBusString *filename, + DBusString *dirname) +{ + int sep; + + _dbus_assert (filename != dirname); + _dbus_assert (filename != NULL); + _dbus_assert (dirname != NULL); + + /* Ignore any separators on the end */ + sep = _dbus_string_get_length (filename); + if (sep == 0) + return _dbus_string_append (dirname, "."); /* empty string passed in */ + + while (sep > 0 && _dbus_string_get_byte (filename, sep - 1) == '/') + --sep; + + _dbus_assert (sep >= 0); + + if (sep == 0) + return _dbus_string_append (dirname, "/"); + + /* Now find the previous separator */ + _dbus_string_find_byte_backward (filename, sep, '/', &sep); + if (sep < 0) + return _dbus_string_append (dirname, "."); + + /* skip multiple separators */ + while (sep > 0 && _dbus_string_get_byte (filename, sep - 1) == '/') + --sep; + + _dbus_assert (sep >= 0); + + if (sep == 0 && + _dbus_string_get_byte (filename, 0) == '/') + return _dbus_string_append (dirname, "/"); + else + return _dbus_string_copy_len (filename, 0, sep - 0, + dirname, _dbus_string_get_length (dirname)); +} +/** @} */ /* DBusString stuff */ + +static void +string_squash_nonprintable (DBusString *str) +{ + char *buf; + int i, len; + + buf = _dbus_string_get_data (str); + len = _dbus_string_get_length (str); + + for (i = 0; i < len; i++) + if (buf[i] == '\0') + buf[i] = ' '; + else if (buf[i] < 0x20 || buf[i] > 127) + buf[i] = '?'; +} + +/** + * Get a printable string describing the command used to execute + * the process with pid. This string should only be used for + * informative purposes such as logging; it may not be trusted. + * + * The command is guaranteed to be printable ASCII and no longer + * than max_len. + * + * @param pid Process id + * @param str Append command to this string + * @param max_len Maximum length of returned command + * @param error return location for errors + * @returns #FALSE on error + */ +dbus_bool_t +_dbus_command_for_pid (unsigned long pid, + DBusString *str, + int max_len, + DBusError *error) +{ + /* This is all Linux-specific for now */ + DBusString path; + DBusString cmdline; + int fd; + + if (!_dbus_string_init (&path)) + { + _DBUS_SET_OOM (error); + return FALSE; + } + + if (!_dbus_string_init (&cmdline)) + { + _DBUS_SET_OOM (error); + _dbus_string_free (&path); + return FALSE; + } + + if (!_dbus_string_append_printf (&path, "/proc/%ld/cmdline", pid)) + goto oom; + + fd = open (_dbus_string_get_const_data (&path), O_RDONLY); + if (fd < 0) + { + dbus_set_error (error, + _dbus_error_from_errno (errno), + "Failed to open \"%s\": %s", + _dbus_string_get_const_data (&path), + _dbus_strerror (errno)); + goto fail; + } + + if (!_dbus_read (fd, &cmdline, max_len)) + { + dbus_set_error (error, + _dbus_error_from_errno (errno), + "Failed to read from \"%s\": %s", + _dbus_string_get_const_data (&path), + _dbus_strerror (errno)); + goto fail; + } + + if (!_dbus_close (fd, error)) + goto fail; + + string_squash_nonprintable (&cmdline); + + if (!_dbus_string_copy (&cmdline, 0, str, _dbus_string_get_length (str))) + goto oom; + + _dbus_string_free (&cmdline); + _dbus_string_free (&path); + return TRUE; +oom: + _DBUS_SET_OOM (error); +fail: + _dbus_string_free (&cmdline); + _dbus_string_free (&path); + return FALSE; +} + diff --git a/src/dbus/dbus-sysdeps-util.c b/src/dbus/dbus-sysdeps-util.c new file mode 100644 index 0000000..448f5c4 --- /dev/null +++ b/src/dbus/dbus-sysdeps-util.c @@ -0,0 +1,176 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-sysdeps-util.c Tests for dbus-sysdeps.h API + * + * Copyright (C) 2002, 2003, 2004, 2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include "dbus-sysdeps.h" +#include "dbus-internals.h" +#include "dbus-string.h" +#include "dbus-test.h" + +#ifdef DBUS_BUILD_TESTS +#include +static void +check_dirname (const char *filename, + const char *dirname) +{ + DBusString f, d; + + _dbus_string_init_const (&f, filename); + + if (!_dbus_string_init (&d)) + _dbus_assert_not_reached ("no memory"); + + if (!_dbus_string_get_dirname (&f, &d)) + _dbus_assert_not_reached ("no memory"); + + if (!_dbus_string_equal_c_str (&d, dirname)) + { + _dbus_warn ("For filename \"%s\" got dirname \"%s\" and expected \"%s\"\n", + filename, + _dbus_string_get_const_data (&d), + dirname); + exit (1); + } + + _dbus_string_free (&d); +} + +static void +check_path_absolute (const char *path, + dbus_bool_t expected) +{ + DBusString p; + + _dbus_string_init_const (&p, path); + + if (_dbus_path_is_absolute (&p) != expected) + { + _dbus_warn ("For path \"%s\" expected absolute = %d got %d\n", + path, expected, _dbus_path_is_absolute (&p)); + exit (1); + } +} + +/** + * Unit test for dbus-sysdeps.c. + * + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_sysdeps_test (void) +{ + DBusString str; + double val; + int pos; + +#ifdef DBUS_WIN + check_dirname ("foo\\bar", "foo"); + check_dirname ("foo\\\\bar", "foo"); + check_dirname ("foo/\\/bar", "foo"); + check_dirname ("foo\\bar/", "foo"); + check_dirname ("foo//bar\\", "foo"); + check_dirname ("foo\\bar/", "foo"); + check_dirname ("foo/bar\\\\", "foo"); + check_dirname ("\\foo", "\\"); + check_dirname ("\\\\foo", "\\"); + check_dirname ("\\", "\\"); + check_dirname ("\\\\", "\\"); + check_dirname ("\\/", "\\"); + check_dirname ("/\\/", "/"); + check_dirname ("c:\\foo\\bar", "c:\\foo"); + check_dirname ("c:\\foo", "c:\\"); + check_dirname ("c:/foo", "c:/"); + check_dirname ("c:\\", "c:\\"); + check_dirname ("c:/", "c:/"); + check_dirname ("", "."); +#else + check_dirname ("foo", "."); + check_dirname ("foo/bar", "foo"); + check_dirname ("foo//bar", "foo"); + check_dirname ("foo///bar", "foo"); + check_dirname ("foo/bar/", "foo"); + check_dirname ("foo//bar/", "foo"); + check_dirname ("foo///bar/", "foo"); + check_dirname ("foo/bar//", "foo"); + check_dirname ("foo//bar////", "foo"); + check_dirname ("foo///bar///////", "foo"); + check_dirname ("/foo", "/"); + check_dirname ("////foo", "/"); + check_dirname ("/foo/bar", "/foo"); + check_dirname ("/foo//bar", "/foo"); + check_dirname ("/foo///bar", "/foo"); + check_dirname ("/", "/"); + check_dirname ("///", "/"); + check_dirname ("", "."); +#endif + + _dbus_string_init_const (&str, "3.5"); + if (!_dbus_string_parse_double (&str, + 0, &val, &pos)) + { + _dbus_warn ("Failed to parse double"); + exit (1); + } + if (ABS(3.5 - val) > 1e-6) + { + _dbus_warn ("Failed to parse 3.5 correctly, got: %f", val); + exit (1); + } + if (pos != 3) + { + _dbus_warn ("_dbus_string_parse_double of \"3.5\" returned wrong position %d", pos); + exit (1); + } + + _dbus_string_init_const (&str, "0xff"); + if (_dbus_string_parse_double (&str, + 0, &val, &pos)) + { + _dbus_warn ("Should not have parsed hex as double\n"); + exit (1); + } + +#ifdef DBUS_WIN + check_path_absolute ("c:/", TRUE); + check_path_absolute ("c:/foo", TRUE); + check_path_absolute ("", FALSE); + check_path_absolute ("foo", FALSE); + check_path_absolute ("foo/bar", FALSE); + check_path_absolute ("", FALSE); + check_path_absolute ("foo\\bar", FALSE); + check_path_absolute ("c:\\", TRUE); + check_path_absolute ("c:\\foo", TRUE); + check_path_absolute ("c:", TRUE); + check_path_absolute ("c:\\foo\\bar", TRUE); + check_path_absolute ("\\", TRUE); + check_path_absolute ("/", TRUE); +#else + check_path_absolute ("/", TRUE); + check_path_absolute ("/foo", TRUE); + check_path_absolute ("", FALSE); + check_path_absolute ("foo", FALSE); + check_path_absolute ("foo/bar", FALSE); +#endif + + return TRUE; +} +#endif /* DBUS_BUILD_TESTS */ diff --git a/src/dbus/dbus-sysdeps.c b/src/dbus/dbus-sysdeps.c new file mode 100644 index 0000000..00a1a3d --- /dev/null +++ b/src/dbus/dbus-sysdeps.c @@ -0,0 +1,1092 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-sysdeps.c Wrappers around system/libc features shared between UNIX and Windows (internal to D-Bus implementation) + * + * Copyright (C) 2002, 2003, 2006 Red Hat, Inc. + * Copyright (C) 2003 CodeFactory AB + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-internals.h" +#include "dbus-sysdeps.h" +#include "dbus-threads.h" +#include "dbus-protocol.h" +#include "dbus-string.h" +#include "dbus-list.h" + +/* NOTE: If you include any unix/windows-specific headers here, you are probably doing something + * wrong and should be putting some code in dbus-sysdeps-unix.c or dbus-sysdeps-win.c. + * + * These are the standard ANSI C headers... + */ +#include +#include +#include +#include + +/* This is UNIX-specific (on windows it's just in stdlib.h I believe) + * but OK since the same stuff does exist on Windows in stdlib.h + * and covered by a configure check. + */ +#ifdef HAVE_ERRNO_H +#include +#endif + +_DBUS_DEFINE_GLOBAL_LOCK (win_fds); +_DBUS_DEFINE_GLOBAL_LOCK (sid_atom_cache); +_DBUS_DEFINE_GLOBAL_LOCK (system_users); + +extern char **environ; + +/** + * @defgroup DBusSysdeps Internal system-dependent API + * @ingroup DBusInternals + * @brief Internal system-dependent API available on UNIX and Windows + * + * The system-dependent API has a dual purpose. First, it encapsulates + * all usage of operating system APIs for ease of auditing and to + * avoid cluttering the rest of the code with bizarre OS quirks and + * headers. Second, it abstracts different operating system APIs for + * portability. + * + * @{ + */ + +/** + * Aborts the program with SIGABRT (dumping core). + */ +void +_dbus_abort (void) +{ + const char *s; + + _dbus_print_backtrace (); + + s = _dbus_getenv ("DBUS_BLOCK_ON_ABORT"); + if (s && *s) + { + /* don't use _dbus_warn here since it can _dbus_abort() */ + fprintf (stderr, " Process %lu sleeping for gdb attach\n", _dbus_pid_for_log ()); + _dbus_sleep_milliseconds (1000 * 180); + } + + abort (); + _dbus_exit (1); /* in case someone manages to ignore SIGABRT ? */ +} + +/** + * Wrapper for setenv(). If the value is #NULL, unsets + * the environment variable. + * + * There is an unfixable memleak in that it is unsafe to + * free memory malloced for use with setenv. This is because + * we can not rely on internal implementation details of + * the underlying libc library. + * + * @param varname name of environment variable + * @param value value of environment variable + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_setenv (const char *varname, + const char *value) +{ + _dbus_assert (varname != NULL); + + if (value == NULL) + { +#ifdef HAVE_UNSETENV + unsetenv (varname); + return TRUE; +#else + char *putenv_value; + size_t len; + + len = strlen (varname); + + /* Use system malloc to avoid memleaks that dbus_malloc + * will get upset about. + */ + + putenv_value = malloc (len + 2); + if (putenv_value == NULL) + return FALSE; + + strcpy (putenv_value, varname); +#if defined(DBUS_WIN) + strcat (putenv_value, "="); +#endif + + return (putenv (putenv_value) == 0); +#endif + } + else + { +#ifdef HAVE_SETENV + return (setenv (varname, value, TRUE) == 0); +#else + char *putenv_value; + size_t len; + size_t varname_len; + size_t value_len; + + varname_len = strlen (varname); + value_len = strlen (value); + + len = varname_len + value_len + 1 /* '=' */ ; + + /* Use system malloc to avoid memleaks that dbus_malloc + * will get upset about. + */ + + putenv_value = malloc (len + 1); + if (putenv_value == NULL) + return FALSE; + + strcpy (putenv_value, varname); + strcpy (putenv_value + varname_len, "="); + strcpy (putenv_value + varname_len + 1, value); + + return (putenv (putenv_value) == 0); +#endif + } +} + +/** + * Wrapper for getenv(). + * + * @param varname name of environment variable + * @returns value of environment variable or #NULL if unset + */ +const char* +_dbus_getenv (const char *varname) +{ + return getenv (varname); +} + +/** + * Wrapper for clearenv(). + * + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_clearenv (void) +{ + dbus_bool_t rc = TRUE; + +#ifdef HAVE_CLEARENV + if (clearenv () != 0) + rc = FALSE; +#else + + if (environ != NULL) + environ[0] = NULL; +#endif + + return rc; +} + +/** + * Gets a #NULL-terminated list of key=value pairs from the + * environment. Use dbus_free_string_array to free it. + * + * @returns the environment or #NULL on OOM + */ +char ** +_dbus_get_environment (void) +{ + int i, length; + char **environment; + + _dbus_assert (environ != NULL); + + for (length = 0; environ[length] != NULL; length++); + + /* Add one for NULL */ + length++; + + environment = dbus_new0 (char *, length); + + if (environment == NULL) + return NULL; + + for (i = 0; environ[i] != NULL; i++) + { + environment[i] = _dbus_strdup (environ[i]); + + if (environment[i] == NULL) + break; + } + + if (environ[i] != NULL) + { + dbus_free_string_array (environment); + environment = NULL; + } + + return environment; +} + +/* + * init a pipe instance. + * + * @param pipe the pipe + * @param fd the file descriptor to init from + */ +void +_dbus_pipe_init (DBusPipe *pipe, + int fd) +{ + pipe->fd_or_handle = fd; +} + +/** + * init a pipe with stdout + * + * @param pipe the pipe + */ +void +_dbus_pipe_init_stdout (DBusPipe *pipe) +{ + _dbus_pipe_init (pipe, 1); +} + +/** + * check if a pipe is valid; pipes can be set invalid, similar to + * a -1 file descriptor. + * + * @param pipe the pipe instance + * @returns #FALSE if pipe is not valid + */ +dbus_bool_t +_dbus_pipe_is_valid(DBusPipe *pipe) +{ + return pipe->fd_or_handle >= 0; +} + +/** + * Check if a pipe is stdout or stderr. + * + * @param pipe the pipe instance + * @returns #TRUE if pipe is one of the standard out/err channels + */ +dbus_bool_t +_dbus_pipe_is_stdout_or_stderr (DBusPipe *pipe) +{ + return pipe->fd_or_handle == 1 || pipe->fd_or_handle == 2; +} + +/** + * Initializes a pipe to an invalid value. + * @param pipe the pipe + */ +void +_dbus_pipe_invalidate (DBusPipe *pipe) +{ + pipe->fd_or_handle = -1; +} + +/** + * Split paths into a list of char strings + * + * @param dirs string with pathes + * @param suffix string concated to each path in dirs + * @param dir_list contains a list of splitted pathes + * return #TRUE is pathes could be splittes,#FALSE in oom case + */ +dbus_bool_t +_dbus_split_paths_and_append (DBusString *dirs, + const char *suffix, + DBusList **dir_list) +{ + int start; + int i; + int len; + char *cpath; + DBusString file_suffix; + + start = 0; + i = 0; + + _dbus_string_init_const (&file_suffix, suffix); + + len = _dbus_string_get_length (dirs); + + while (_dbus_string_find (dirs, start, _DBUS_PATH_SEPARATOR, &i)) + { + DBusString path; + + if (!_dbus_string_init (&path)) + goto oom; + + if (!_dbus_string_copy_len (dirs, + start, + i - start, + &path, + 0)) + { + _dbus_string_free (&path); + goto oom; + } + + _dbus_string_chop_white (&path); + + /* check for an empty path */ + if (_dbus_string_get_length (&path) == 0) + goto next; + + if (!_dbus_concat_dir_and_file (&path, + &file_suffix)) + { + _dbus_string_free (&path); + goto oom; + } + + if (!_dbus_string_copy_data(&path, &cpath)) + { + _dbus_string_free (&path); + goto oom; + } + + if (!_dbus_list_append (dir_list, cpath)) + { + _dbus_string_free (&path); + dbus_free (cpath); + goto oom; + } + + next: + _dbus_string_free (&path); + start = i + 1; + } + + if (start != len) + { + DBusString path; + + if (!_dbus_string_init (&path)) + goto oom; + + if (!_dbus_string_copy_len (dirs, + start, + len - start, + &path, + 0)) + { + _dbus_string_free (&path); + goto oom; + } + + if (!_dbus_concat_dir_and_file (&path, + &file_suffix)) + { + _dbus_string_free (&path); + goto oom; + } + + if (!_dbus_string_copy_data(&path, &cpath)) + { + _dbus_string_free (&path); + goto oom; + } + + if (!_dbus_list_append (dir_list, cpath)) + { + _dbus_string_free (&path); + dbus_free (cpath); + goto oom; + } + + _dbus_string_free (&path); + } + + return TRUE; + + oom: + _dbus_list_foreach (dir_list, (DBusForeachFunction)dbus_free, NULL); + _dbus_list_clear (dir_list); + return FALSE; +} + +/** @} */ + +/** + * @addtogroup DBusString + * + * @{ + */ +/** + * Appends an integer to a DBusString. + * + * @param str the string + * @param value the integer value + * @returns #FALSE if not enough memory or other failure. + */ +dbus_bool_t +_dbus_string_append_int (DBusString *str, + long value) +{ + /* this calculation is from comp.lang.c faq */ +#define MAX_LONG_LEN ((sizeof (long) * 8 + 2) / 3 + 1) /* +1 for '-' */ + int orig_len; + int i; + char *buf; + + orig_len = _dbus_string_get_length (str); + + if (!_dbus_string_lengthen (str, MAX_LONG_LEN)) + return FALSE; + + buf = _dbus_string_get_data_len (str, orig_len, MAX_LONG_LEN); + + snprintf (buf, MAX_LONG_LEN, "%ld", value); + + i = 0; + while (*buf) + { + ++buf; + ++i; + } + + _dbus_string_shorten (str, MAX_LONG_LEN - i); + + return TRUE; +} + +/** + * Appends an unsigned integer to a DBusString. + * + * @param str the string + * @param value the integer value + * @returns #FALSE if not enough memory or other failure. + */ +dbus_bool_t +_dbus_string_append_uint (DBusString *str, + unsigned long value) +{ + /* this is wrong, but definitely on the high side. */ +#define MAX_ULONG_LEN (MAX_LONG_LEN * 2) + int orig_len; + int i; + char *buf; + + orig_len = _dbus_string_get_length (str); + + if (!_dbus_string_lengthen (str, MAX_ULONG_LEN)) + return FALSE; + + buf = _dbus_string_get_data_len (str, orig_len, MAX_ULONG_LEN); + + snprintf (buf, MAX_ULONG_LEN, "%lu", value); + + i = 0; + while (*buf) + { + ++buf; + ++i; + } + + _dbus_string_shorten (str, MAX_ULONG_LEN - i); + + return TRUE; +} + +#ifdef DBUS_BUILD_TESTS +/** + * Appends a double to a DBusString. + * + * @param str the string + * @param value the floating point value + * @returns #FALSE if not enough memory or other failure. + */ +dbus_bool_t +_dbus_string_append_double (DBusString *str, + double value) +{ +#define MAX_DOUBLE_LEN 64 /* this is completely made up :-/ */ + int orig_len; + char *buf; + int i; + + orig_len = _dbus_string_get_length (str); + + if (!_dbus_string_lengthen (str, MAX_DOUBLE_LEN)) + return FALSE; + + buf = _dbus_string_get_data_len (str, orig_len, MAX_DOUBLE_LEN); + + snprintf (buf, MAX_LONG_LEN, "%g", value); + + i = 0; + while (*buf) + { + ++buf; + ++i; + } + + _dbus_string_shorten (str, MAX_DOUBLE_LEN - i); + + return TRUE; +} +#endif /* DBUS_BUILD_TESTS */ + +/** + * Parses an integer contained in a DBusString. Either return parameter + * may be #NULL if you aren't interested in it. The integer is parsed + * and stored in value_return. Return parameters are not initialized + * if the function returns #FALSE. + * + * @param str the string + * @param start the byte index of the start of the integer + * @param value_return return location of the integer value or #NULL + * @param end_return return location of the end of the integer, or #NULL + * @returns #TRUE on success + */ +dbus_bool_t +_dbus_string_parse_int (const DBusString *str, + int start, + long *value_return, + int *end_return) +{ + long v; + const char *p; + char *end; + + p = _dbus_string_get_const_data_len (str, start, + _dbus_string_get_length (str) - start); + + end = NULL; + errno = 0; + v = strtol (p, &end, 0); + if (end == NULL || end == p || errno != 0) + return FALSE; + + if (value_return) + *value_return = v; + if (end_return) + *end_return = start + (end - p); + + return TRUE; +} + +/** + * Parses an unsigned integer contained in a DBusString. Either return + * parameter may be #NULL if you aren't interested in it. The integer + * is parsed and stored in value_return. Return parameters are not + * initialized if the function returns #FALSE. + * + * @param str the string + * @param start the byte index of the start of the integer + * @param value_return return location of the integer value or #NULL + * @param end_return return location of the end of the integer, or #NULL + * @returns #TRUE on success + */ +dbus_bool_t +_dbus_string_parse_uint (const DBusString *str, + int start, + unsigned long *value_return, + int *end_return) +{ + unsigned long v; + const char *p; + char *end; + + p = _dbus_string_get_const_data_len (str, start, + _dbus_string_get_length (str) - start); + + end = NULL; + errno = 0; + v = strtoul (p, &end, 0); + if (end == NULL || end == p || errno != 0) + return FALSE; + + if (value_return) + *value_return = v; + if (end_return) + *end_return = start + (end - p); + + return TRUE; +} + +#ifdef DBUS_BUILD_TESTS +static dbus_bool_t +ascii_isspace (char c) +{ + return (c == ' ' || + c == '\f' || + c == '\n' || + c == '\r' || + c == '\t' || + c == '\v'); +} +#endif /* DBUS_BUILD_TESTS */ + +#ifdef DBUS_BUILD_TESTS +static dbus_bool_t +ascii_isdigit (char c) +{ + return c >= '0' && c <= '9'; +} +#endif /* DBUS_BUILD_TESTS */ + +#ifdef DBUS_BUILD_TESTS +static dbus_bool_t +ascii_isxdigit (char c) +{ + return (ascii_isdigit (c) || + (c >= 'a' && c <= 'f') || + (c >= 'A' && c <= 'F')); +} +#endif /* DBUS_BUILD_TESTS */ + +#ifdef DBUS_BUILD_TESTS +/* Calls strtod in a locale-independent fashion, by looking at + * the locale data and patching the decimal comma to a point. + * + * Relicensed from glib. + */ +static double +ascii_strtod (const char *nptr, + char **endptr) +{ + /* FIXME: The Win32 C library's strtod() doesn't handle hex. + * Presumably many Unixes don't either. + */ + + char *fail_pos; + double val; + struct lconv *locale_data; + const char *decimal_point; + int decimal_point_len; + const char *p, *decimal_point_pos; + const char *end = NULL; /* Silence gcc */ + + fail_pos = NULL; + + locale_data = localeconv (); + decimal_point = locale_data->decimal_point; + decimal_point_len = strlen (decimal_point); + + _dbus_assert (decimal_point_len != 0); + + decimal_point_pos = NULL; + if (decimal_point[0] != '.' || + decimal_point[1] != 0) + { + p = nptr; + /* Skip leading space */ + while (ascii_isspace (*p)) + p++; + + /* Skip leading optional sign */ + if (*p == '+' || *p == '-') + p++; + + if (p[0] == '0' && + (p[1] == 'x' || p[1] == 'X')) + { + p += 2; + /* HEX - find the (optional) decimal point */ + + while (ascii_isxdigit (*p)) + p++; + + if (*p == '.') + { + decimal_point_pos = p++; + + while (ascii_isxdigit (*p)) + p++; + + if (*p == 'p' || *p == 'P') + p++; + if (*p == '+' || *p == '-') + p++; + while (ascii_isdigit (*p)) + p++; + end = p; + } + } + else + { + while (ascii_isdigit (*p)) + p++; + + if (*p == '.') + { + decimal_point_pos = p++; + + while (ascii_isdigit (*p)) + p++; + + if (*p == 'e' || *p == 'E') + p++; + if (*p == '+' || *p == '-') + p++; + while (ascii_isdigit (*p)) + p++; + end = p; + } + } + /* For the other cases, we need not convert the decimal point */ + } + + /* Set errno to zero, so that we can distinguish zero results + and underflows */ + errno = 0; + + if (decimal_point_pos) + { + char *copy, *c; + + /* We need to convert the '.' to the locale specific decimal point */ + copy = dbus_malloc (end - nptr + 1 + decimal_point_len); + + c = copy; + memcpy (c, nptr, decimal_point_pos - nptr); + c += decimal_point_pos - nptr; + memcpy (c, decimal_point, decimal_point_len); + c += decimal_point_len; + memcpy (c, decimal_point_pos + 1, end - (decimal_point_pos + 1)); + c += end - (decimal_point_pos + 1); + *c = 0; + + val = strtod (copy, &fail_pos); + + if (fail_pos) + { + if (fail_pos > decimal_point_pos) + fail_pos = (char *)nptr + (fail_pos - copy) - (decimal_point_len - 1); + else + fail_pos = (char *)nptr + (fail_pos - copy); + } + + dbus_free (copy); + + } + else + val = strtod (nptr, &fail_pos); + + if (endptr) + *endptr = fail_pos; + + return val; +} +#endif /* DBUS_BUILD_TESTS */ + +#ifdef DBUS_BUILD_TESTS +/** + * Parses a floating point number contained in a DBusString. Either + * return parameter may be #NULL if you aren't interested in it. The + * integer is parsed and stored in value_return. Return parameters are + * not initialized if the function returns #FALSE. + * + * @param str the string + * @param start the byte index of the start of the float + * @param value_return return location of the float value or #NULL + * @param end_return return location of the end of the float, or #NULL + * @returns #TRUE on success + */ +dbus_bool_t +_dbus_string_parse_double (const DBusString *str, + int start, + double *value_return, + int *end_return) +{ + double v; + const char *p; + char *end; + + p = _dbus_string_get_const_data_len (str, start, + _dbus_string_get_length (str) - start); + + /* parsing hex works on linux but isn't portable, so intercept it + * here to get uniform behavior. + */ + if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) + return FALSE; + + end = NULL; + errno = 0; + v = ascii_strtod (p, &end); + if (end == NULL || end == p || errno != 0) + return FALSE; + + if (value_return) + *value_return = v; + if (end_return) + *end_return = start + (end - p); + + return TRUE; +} +#endif /* DBUS_BUILD_TESTS */ + +/** @} */ /* DBusString group */ + +/** + * @addtogroup DBusInternalsUtils + * @{ + */ + +void +_dbus_generate_pseudorandom_bytes_buffer (char *buffer, + int n_bytes) +{ + long tv_usec; + int i; + + /* fall back to pseudorandom */ + _dbus_verbose ("Falling back to pseudorandom for %d bytes\n", + n_bytes); + + _dbus_get_current_time (NULL, &tv_usec); + srand (tv_usec); + + i = 0; + while (i < n_bytes) + { + double r; + unsigned int b; + + r = rand (); + b = (r / (double) RAND_MAX) * 255.0; + + buffer[i] = b; + + ++i; + } +} + +/** + * Fills n_bytes of the given buffer with random bytes. + * + * @param buffer an allocated buffer + * @param n_bytes the number of bytes in buffer to write to + */ +void +_dbus_generate_random_bytes_buffer (char *buffer, + int n_bytes) +{ + DBusString str; + + if (!_dbus_string_init (&str)) + { + _dbus_generate_pseudorandom_bytes_buffer (buffer, n_bytes); + return; + } + + if (!_dbus_generate_random_bytes (&str, n_bytes)) + { + _dbus_string_free (&str); + _dbus_generate_pseudorandom_bytes_buffer (buffer, n_bytes); + return; + } + + _dbus_string_copy_to_buffer (&str, buffer, n_bytes); + + _dbus_string_free (&str); +} + +/** + * Generates the given number of random bytes, where the bytes are + * chosen from the alphanumeric ASCII subset. + * + * @param str the string + * @param n_bytes the number of random ASCII bytes to append to string + * @returns #TRUE on success, #FALSE if no memory or other failure + */ +dbus_bool_t +_dbus_generate_random_ascii (DBusString *str, + int n_bytes) +{ + static const char letters[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"; + int i; + int len; + + if (!_dbus_generate_random_bytes (str, n_bytes)) + return FALSE; + + len = _dbus_string_get_length (str); + i = len - n_bytes; + while (i < len) + { + _dbus_string_set_byte (str, i, + letters[_dbus_string_get_byte (str, i) % + (sizeof (letters) - 1)]); + + ++i; + } + + _dbus_assert (_dbus_string_validate_ascii (str, len - n_bytes, + n_bytes)); + + return TRUE; +} + +/** + * Converts a UNIX or Windows errno + * into a #DBusError name. + * + * @todo should cover more errnos, specifically those + * from open(). + * + * @param error_number the errno. + * @returns an error name + */ +const char* +_dbus_error_from_errno (int error_number) +{ + switch (error_number) + { + case 0: + return DBUS_ERROR_FAILED; + +#ifdef EPROTONOSUPPORT + case EPROTONOSUPPORT: + return DBUS_ERROR_NOT_SUPPORTED; +#endif +#ifdef EAFNOSUPPORT + case EAFNOSUPPORT: + return DBUS_ERROR_NOT_SUPPORTED; +#endif +#ifdef ENFILE + case ENFILE: + return DBUS_ERROR_LIMITS_EXCEEDED; /* kernel out of memory */ +#endif +#ifdef EMFILE + case EMFILE: + return DBUS_ERROR_LIMITS_EXCEEDED; +#endif +#ifdef EACCES + case EACCES: + return DBUS_ERROR_ACCESS_DENIED; +#endif +#ifdef EPERM + case EPERM: + return DBUS_ERROR_ACCESS_DENIED; +#endif +#ifdef ENOBUFS + case ENOBUFS: + return DBUS_ERROR_NO_MEMORY; +#endif +#ifdef ENOMEM + case ENOMEM: + return DBUS_ERROR_NO_MEMORY; +#endif +#ifdef EINVAL + case EINVAL: + return DBUS_ERROR_FAILED; +#endif +#ifdef EBADF + case EBADF: + return DBUS_ERROR_FAILED; +#endif +#ifdef EFAULT + case EFAULT: + return DBUS_ERROR_FAILED; +#endif +#ifdef ENOTSOCK + case ENOTSOCK: + return DBUS_ERROR_FAILED; +#endif +#ifdef EISCONN + case EISCONN: + return DBUS_ERROR_FAILED; +#endif +#ifdef ECONNREFUSED + case ECONNREFUSED: + return DBUS_ERROR_NO_SERVER; +#endif +#ifdef ETIMEDOUT + case ETIMEDOUT: + return DBUS_ERROR_TIMEOUT; +#endif +#ifdef ENETUNREACH + case ENETUNREACH: + return DBUS_ERROR_NO_NETWORK; +#endif +#ifdef EADDRINUSE + case EADDRINUSE: + return DBUS_ERROR_ADDRESS_IN_USE; +#endif +#ifdef EEXIST + case EEXIST: + return DBUS_ERROR_FILE_EXISTS; +#endif +#ifdef ENOENT + case ENOENT: + return DBUS_ERROR_FILE_NOT_FOUND; +#endif + } + + return DBUS_ERROR_FAILED; +} + +/** + * Assign 0 to the global errno variable + */ +void +_dbus_set_errno_to_zero (void) +{ + errno = 0; +} + +/** + * See if errno is set + * @returns #TRUE if errno is not 0 + */ +dbus_bool_t +_dbus_get_is_errno_nonzero (void) +{ + return errno != 0; +} + +/** + * See if errno is ENOMEM + * @returns #TRUE if errno == ENOMEM + */ +dbus_bool_t +_dbus_get_is_errno_enomem (void) +{ + return errno == ENOMEM; +} + +/** + * See if errno is EINTR + * @returns #TRUE if errno == EINTR + */ +dbus_bool_t +_dbus_get_is_errno_eintr (void) +{ + return errno == EINTR; +} + +/** + * Get error message from errno + * @returns _dbus_strerror(errno) + */ +const char* +_dbus_strerror_from_errno (void) +{ + return _dbus_strerror (errno); +} + +/** @} end of sysdeps */ + +/* tests in dbus-sysdeps-util.c */ diff --git a/src/dbus/dbus-sysdeps.h b/src/dbus/dbus-sysdeps.h new file mode 100644 index 0000000..b766f3f --- /dev/null +++ b/src/dbus/dbus-sysdeps.h @@ -0,0 +1,505 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-sysdeps.h Wrappers around system/libc features (internal to D-Bus implementation) + * + * Copyright (C) 2002, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef DBUS_SYSDEPS_H +#define DBUS_SYSDEPS_H + +#include + +#include + +/* this is perhaps bogus, but strcmp() etc. are faster if we use the + * stuff straight out of string.h, so have this here for now. + */ +#include +#include + + +/* AIX sys/poll.h does #define events reqevents, and other + * wonderousness, so must include sys/poll before declaring + * DBusPollFD + */ +#ifdef HAVE_POLL +#include +#endif + +DBUS_BEGIN_DECLS + +#ifdef DBUS_WIN +#define _DBUS_PATH_SEPARATOR ";" +#else +#define _DBUS_PATH_SEPARATOR ":" +#endif + +/* Forward declarations */ + +/** An opaque string type */ +typedef struct DBusString DBusString; + +/** An opaque list type */ +typedef struct DBusList DBusList; + +/** Object that contains a list of credentials such as UNIX or Windows user ID */ +typedef struct DBusCredentials DBusCredentials; + +/** + * @addtogroup DBusSysdeps + * + * @{ + */ + +/* The idea of this file is to encapsulate everywhere that we're + * relying on external libc features, for ease of security + * auditing. The idea is from vsftpd. This also gives us a chance to + * make things more convenient to use, e.g. by reading into a + * DBusString. Operating system headers aren't intended to be used + * outside of this file and a limited number of others (such as + * dbus-memory.c) + */ + +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) +#define _DBUS_GNUC_PRINTF( format_idx, arg_idx ) \ + __attribute__((__format__ (__printf__, format_idx, arg_idx))) +#define _DBUS_GNUC_NORETURN \ + __attribute__((__noreturn__)) +#else /* !__GNUC__ */ +#define _DBUS_GNUC_PRINTF( format_idx, arg_idx ) +#define _DBUS_GNUC_NORETURN +#endif /* !__GNUC__ */ + +/** @def _DBUS_GNUC_PRINTF + * used to tell gcc about printf format strings + */ +/** @def _DBUS_GNUC_NORETURN + * used to tell gcc about functions that never return, such as _dbus_abort() + */ + +void _dbus_abort (void) _DBUS_GNUC_NORETURN; + +const char* _dbus_getenv (const char *varname); +dbus_bool_t _dbus_setenv (const char *varname, + const char *value); +dbus_bool_t _dbus_clearenv (void); +char ** _dbus_get_environment (void); + +/** A process ID */ +typedef unsigned long dbus_pid_t; +/** A user ID */ +typedef unsigned long dbus_uid_t; +/** A group ID */ +typedef unsigned long dbus_gid_t; + +/** an invalid PID used to represent an uninitialized dbus_pid_t field */ +#define DBUS_PID_UNSET ((dbus_pid_t) -1) +/** an invalid UID used to represent an uninitialized dbus_uid_t field */ +#define DBUS_UID_UNSET ((dbus_uid_t) -1) +/** an invalid GID used to represent an uninitialized dbus_gid_t field */ +#define DBUS_GID_UNSET ((dbus_gid_t) -1) + +/** an appropriate printf format for dbus_pid_t */ +#define DBUS_PID_FORMAT "%lu" +/** an appropriate printf format for dbus_uid_t */ +#define DBUS_UID_FORMAT "%lu" +/** an appropriate printf format for dbus_gid_t */ +#define DBUS_GID_FORMAT "%lu" + + +/** + * Socket interface + * + * @todo Use for the file descriptors a struct + * - struct DBusSocket{ int d; }; - + * instead of int to get type-safety which + * will be checked by the compiler. + * + */ + +dbus_bool_t _dbus_open_tcp_socket (int *fd, + DBusError *error); +dbus_bool_t _dbus_close_socket (int fd, + DBusError *error); +int _dbus_read_socket (int fd, + DBusString *buffer, + int count); +int _dbus_write_socket (int fd, + const DBusString *buffer, + int start, + int len); +int _dbus_write_socket_two (int fd, + const DBusString *buffer1, + int start1, + int len1, + const DBusString *buffer2, + int start2, + int len2); +int _dbus_connect_tcp_socket (const char *host, + const char *port, + const char *family, + DBusError *error); +int _dbus_listen_tcp_socket (const char *host, + const char *port, + const char *family, + DBusString *retport, + int **fds_p, + DBusError *error); +int _dbus_accept (int listen_fd); + + +dbus_bool_t _dbus_read_credentials_socket (int client_fd, + DBusCredentials *credentials, + DBusError *error); +dbus_bool_t _dbus_send_credentials_socket (int server_fd, + DBusError *error); + +dbus_bool_t _dbus_credentials_add_from_user (DBusCredentials *credentials, + const DBusString *username); +dbus_bool_t _dbus_credentials_add_from_current_process (DBusCredentials *credentials); +dbus_bool_t _dbus_append_user_from_current_process (DBusString *str); + +dbus_bool_t _dbus_parse_unix_user_from_config (const DBusString *username, + dbus_uid_t *uid_p); +dbus_bool_t _dbus_parse_unix_group_from_config (const DBusString *groupname, + dbus_gid_t *gid_p); +dbus_bool_t _dbus_unix_groups_from_uid (dbus_uid_t uid, + dbus_gid_t **group_ids, + int *n_group_ids); +dbus_bool_t _dbus_unix_user_is_at_console (dbus_uid_t uid, + DBusError *error); +dbus_bool_t _dbus_unix_user_is_process_owner (dbus_uid_t uid); +dbus_bool_t _dbus_windows_user_is_process_owner (const char *windows_sid); + +dbus_bool_t _dbus_append_keyring_directory_for_credentials (DBusString *directory, + DBusCredentials *credentials); + +/** Opaque type representing an atomically-modifiable integer + * that can be used from multiple threads. + */ +typedef struct DBusAtomic DBusAtomic; + +/** + * An atomic integer safe to increment or decrement from multiple threads. + */ +struct DBusAtomic +{ +#ifdef DBUS_WIN + volatile long value; /**< Value of the atomic integer. */ +#else + volatile dbus_int32_t value; /**< Value of the atomic integer. */ +#endif +}; + +/* The value we get from autofoo is in the form of a cpp expression; + * convert that to a conventional defined/undef switch. (We can't get + * the conventional defined/undef because of multiarch builds only running + * ./configure once, on Darwin.) */ +#if DBUS_HAVE_ATOMIC_INT_COND +# define DBUS_HAVE_ATOMIC_INT 1 +#else +# undef DBUS_HAVE_ATOMIC_INT +#endif + +dbus_int32_t _dbus_atomic_inc (DBusAtomic *atomic); +dbus_int32_t _dbus_atomic_dec (DBusAtomic *atomic); + + +/* AIX uses different values for poll */ + +#ifdef _AIX +/** There is data to read */ +#define _DBUS_POLLIN 0x0001 +/** There is urgent data to read */ +#define _DBUS_POLLPRI 0x0004 +/** Writing now will not block */ +#define _DBUS_POLLOUT 0x0002 +/** Error condition */ +#define _DBUS_POLLERR 0x4000 +/** Hung up */ +#define _DBUS_POLLHUP 0x2000 +/** Invalid request: fd not open */ +#define _DBUS_POLLNVAL 0x8000 +#else +/** There is data to read */ +#define _DBUS_POLLIN 0x0001 +/** There is urgent data to read */ +#define _DBUS_POLLPRI 0x0002 +/** Writing now will not block */ +#define _DBUS_POLLOUT 0x0004 +/** Error condition */ +#define _DBUS_POLLERR 0x0008 +/** Hung up */ +#define _DBUS_POLLHUP 0x0010 +/** Invalid request: fd not open */ +#define _DBUS_POLLNVAL 0x0020 +#endif + +/** + * A portable struct pollfd wrapper. + */ +typedef struct +{ + int fd; /**< File descriptor */ + short events; /**< Events to poll for */ + short revents; /**< Events that occurred */ +} DBusPollFD; + +int _dbus_poll (DBusPollFD *fds, + int n_fds, + int timeout_milliseconds); + +void _dbus_sleep_milliseconds (int milliseconds); + +void _dbus_get_current_time (long *tv_sec, + long *tv_usec); + +/** + * File/directory interface + */ +dbus_bool_t _dbus_file_exists (const char *file); +dbus_bool_t _dbus_file_get_contents (DBusString *str, + const DBusString *filename, + DBusError *error); +dbus_bool_t _dbus_string_save_to_file (const DBusString *str, + const DBusString *filename, + DBusError *error); + +dbus_bool_t _dbus_make_file_world_readable (const DBusString *filename, + DBusError *error); + +dbus_bool_t _dbus_create_file_exclusively (const DBusString *filename, + DBusError *error); +dbus_bool_t _dbus_delete_file (const DBusString *filename, + DBusError *error); +dbus_bool_t _dbus_create_directory (const DBusString *filename, + DBusError *error); +dbus_bool_t _dbus_delete_directory (const DBusString *filename, + DBusError *error); + +dbus_bool_t _dbus_concat_dir_and_file (DBusString *dir, + const DBusString *next_component); +dbus_bool_t _dbus_string_get_dirname (const DBusString *filename, + DBusString *dirname); +dbus_bool_t _dbus_path_is_absolute (const DBusString *filename); + +dbus_bool_t _dbus_get_standard_session_servicedirs (DBusList **dirs); +dbus_bool_t _dbus_get_standard_system_servicedirs (DBusList **dirs); + +dbus_bool_t _dbus_append_system_config_file (DBusString *str); +dbus_bool_t _dbus_append_session_config_file (DBusString *str); + +typedef struct { + int fd_or_handle; +} DBusPipe; + +void _dbus_pipe_init (DBusPipe *pipe, + int fd); +void _dbus_pipe_init_stdout (DBusPipe *pipe); +int _dbus_pipe_write (DBusPipe *pipe, + const DBusString *buffer, + int start, + int len, + DBusError *error); +int _dbus_pipe_close (DBusPipe *pipe, + DBusError *error); +dbus_bool_t _dbus_pipe_is_valid (DBusPipe *pipe); +void _dbus_pipe_invalidate (DBusPipe *pipe); +dbus_bool_t _dbus_pipe_is_stdout_or_stderr (DBusPipe *pipe); + + +/** Opaque type for reading a directory listing */ +typedef struct DBusDirIter DBusDirIter; + +DBusDirIter* _dbus_directory_open (const DBusString *filename, + DBusError *error); +dbus_bool_t _dbus_directory_get_next_file (DBusDirIter *iter, + DBusString *filename, + DBusError *error); +void _dbus_directory_close (DBusDirIter *iter); + +dbus_bool_t _dbus_check_dir_is_private_to_user (DBusString *dir, + DBusError *error); + +void _dbus_fd_set_close_on_exec (int fd); + +const char* _dbus_get_tmpdir (void); + +/** + * Random numbers + */ +void _dbus_generate_pseudorandom_bytes_buffer (char *buffer, + int n_bytes); +void _dbus_generate_random_bytes_buffer (char *buffer, + int n_bytes); +dbus_bool_t _dbus_generate_random_bytes (DBusString *str, + int n_bytes); +dbus_bool_t _dbus_generate_random_ascii (DBusString *str, + int n_bytes); + +const char* _dbus_error_from_errno (int error_number); + +void _dbus_set_errno_to_zero (void); +dbus_bool_t _dbus_get_is_errno_nonzero (void); +dbus_bool_t _dbus_get_is_errno_eagain_or_ewouldblock (void); +dbus_bool_t _dbus_get_is_errno_enomem (void); +dbus_bool_t _dbus_get_is_errno_eintr (void); +const char* _dbus_strerror_from_errno (void); + +void _dbus_disable_sigpipe (void); + + +void _dbus_exit (int code) _DBUS_GNUC_NORETURN; + +int _dbus_printf_string_upper_bound (const char *format, + va_list args); + + +/** + * Portable struct with stat() results + */ +typedef struct +{ + unsigned long mode; /**< File mode */ + unsigned long nlink; /**< Number of hard links */ + dbus_uid_t uid; /**< User owning file */ + dbus_gid_t gid; /**< Group owning file */ + unsigned long size; /**< Size of file */ + unsigned long atime; /**< Access time */ + unsigned long mtime; /**< Modify time */ + unsigned long ctime; /**< Creation time */ +} DBusStat; + +dbus_bool_t _dbus_stat (const DBusString *filename, + DBusStat *statbuf, + DBusError *error); +dbus_bool_t _dbus_full_duplex_pipe (int *fd1, + int *fd2, + dbus_bool_t blocking, + DBusError *error); + +void _dbus_print_backtrace (void); + +dbus_bool_t _dbus_become_daemon (const DBusString *pidfile, + DBusPipe *print_pid_pipe, + DBusError *error, + dbus_bool_t keep_umask); + +dbus_bool_t _dbus_verify_daemon_user (const char *user); +dbus_bool_t _dbus_change_to_daemon_user (const char *user, + DBusError *error); + +dbus_bool_t _dbus_write_pid_to_file_and_pipe (const DBusString *pidfile, + DBusPipe *print_pid_pipe, + dbus_pid_t pid_to_write, + DBusError *error); + +dbus_bool_t _dbus_command_for_pid (unsigned long pid, + DBusString *str, + int max_len, + DBusError *error); + +/** A UNIX signal handler */ +typedef void (* DBusSignalHandler) (int sig); + +void _dbus_set_signal_handler (int sig, + DBusSignalHandler handler); + +dbus_bool_t _dbus_user_at_console (const char *username, + DBusError *error); + +void _dbus_init_system_log (void); +void _dbus_log_info (const char *msg, va_list args); +void _dbus_log_security (const char *msg, va_list args); + +/* Define DBUS_VA_COPY() to do the right thing for copying va_list variables. + * config.h may have already defined DBUS_VA_COPY as va_copy or __va_copy. + */ +#if !defined (DBUS_VA_COPY) +# if defined (__GNUC__) && defined (__PPC__) && (defined (_CALL_SYSV) || defined (_WIN32)) +# define DBUS_VA_COPY(ap1, ap2) (*(ap1) = *(ap2)) +# elif defined (DBUS_VA_COPY_AS_ARRAY) +# define DBUS_VA_COPY(ap1, ap2) memcpy ((ap1), (ap2), sizeof (va_list)) +# else /* va_list is a pointer */ +# define DBUS_VA_COPY(ap1, ap2) ((ap1) = (ap2)) +# endif /* va_list is a pointer */ +#endif /* !DBUS_VA_COPY */ + + +/** + * Casts a primitive C type to a byte array and then indexes + * a particular byte of the array. + */ +#define _DBUS_BYTE_OF_PRIMITIVE(p, i) \ + (((const char*)&(p))[(i)]) +/** On x86 there is an 80-bit FPU, and if you do "a == b" it may have a + * or b in an 80-bit register, thus failing to compare the two 64-bit + * doubles for bitwise equality. So this macro compares the two doubles + * bitwise. + */ +#define _DBUS_DOUBLES_BITWISE_EQUAL(a, b) \ + (_DBUS_BYTE_OF_PRIMITIVE (a, 0) == _DBUS_BYTE_OF_PRIMITIVE (b, 0) && \ + _DBUS_BYTE_OF_PRIMITIVE (a, 1) == _DBUS_BYTE_OF_PRIMITIVE (b, 1) && \ + _DBUS_BYTE_OF_PRIMITIVE (a, 2) == _DBUS_BYTE_OF_PRIMITIVE (b, 2) && \ + _DBUS_BYTE_OF_PRIMITIVE (a, 3) == _DBUS_BYTE_OF_PRIMITIVE (b, 3) && \ + _DBUS_BYTE_OF_PRIMITIVE (a, 4) == _DBUS_BYTE_OF_PRIMITIVE (b, 4) && \ + _DBUS_BYTE_OF_PRIMITIVE (a, 5) == _DBUS_BYTE_OF_PRIMITIVE (b, 5) && \ + _DBUS_BYTE_OF_PRIMITIVE (a, 6) == _DBUS_BYTE_OF_PRIMITIVE (b, 6) && \ + _DBUS_BYTE_OF_PRIMITIVE (a, 7) == _DBUS_BYTE_OF_PRIMITIVE (b, 7)) + +dbus_bool_t _dbus_get_autolaunch_address (DBusString *address, + DBusError *error); + +/** Type representing a universally unique ID + * @todo rename to UUID instead of GUID + */ +typedef union DBusGUID DBusGUID; + +dbus_bool_t _dbus_read_local_machine_uuid (DBusGUID *machine_id, + dbus_bool_t create_if_not_found, + DBusError *error); + +/** + * Initialize threads as in dbus_threads_init_default(), appropriately + * for the platform. + * @returns #FALSE if no memory + */ +dbus_bool_t _dbus_threads_init_platform_specific (void); + +dbus_bool_t _dbus_split_paths_and_append (DBusString *dirs, + const char *suffix, + DBusList **dir_list); + +unsigned long _dbus_pid_for_log (void); + +/* FIXME move back to dbus-sysdeps-unix.h probably - + * the PID file handling just needs a little more abstraction + * in the bus daemon first. + */ +dbus_pid_t _dbus_getpid (void); + +void _dbus_flush_caches (void); + +/** @} */ + +DBUS_END_DECLS + +#endif /* DBUS_SYSDEPS_H */ diff --git a/src/dbus/dbus-test-main.c b/src/dbus/dbus-test-main.c new file mode 100644 index 0000000..e7fcc67 --- /dev/null +++ b/src/dbus/dbus-test-main.c @@ -0,0 +1,54 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-test.c Program to run all tests + * + * Copyright (C) 2002 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#include "dbus-types.h" +#include "dbus-test.h" +#include +#include +#include + +int +main (int argc, + char **argv) +{ + const char *test_data_dir; + const char *specific_test; + + setlocale(LC_ALL, ""); + + + if (argc > 1) + test_data_dir = argv[1]; + else + test_data_dir = NULL; + + if (argc > 2) + specific_test = argv[2]; + else + specific_test = NULL; + + dbus_internal_do_not_use_run_tests (test_data_dir, specific_test); + + return 0; +} diff --git a/src/dbus/dbus-test.c b/src/dbus/dbus-test.c new file mode 100644 index 0000000..180235f --- /dev/null +++ b/src/dbus/dbus-test.c @@ -0,0 +1,190 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-test.c Program to run all tests + * + * Copyright (C) 2002, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include "dbus-test.h" +#include "dbus-sysdeps.h" +#include "dbus-internals.h" +#include +#include + +#ifdef DBUS_BUILD_TESTS +static void +die (const char *failure) +{ + fprintf (stderr, "Unit test failed: %s\n", failure); + exit (1); +} + +static void +check_memleaks (void) +{ + dbus_shutdown (); + + printf ("%s: checking for memleaks\n", "dbus-test"); + if (_dbus_get_malloc_blocks_outstanding () != 0) + { + _dbus_warn ("%d dbus_malloc blocks were not freed\n", + _dbus_get_malloc_blocks_outstanding ()); + die ("memleaks"); + } +} + +typedef dbus_bool_t (*TestFunc)(void); +typedef dbus_bool_t (*TestDataFunc)(const char *data); + +static void +run_test (const char *test_name, + const char *specific_test, + TestFunc test) +{ + if (!specific_test || strcmp (specific_test, test_name) == 0) + { + printf ("%s: running %s tests\n", "dbus-test", test_name); + if (!test ()) + die (test_name); + } + + check_memleaks (); +} + +static void +run_data_test (const char *test_name, + const char *specific_test, + TestDataFunc test, + const char *test_data_dir) +{ + if (!specific_test || strcmp (specific_test, test_name) == 0) + { + printf ("%s: running %s tests\n", "dbus-test", test_name); + if (!test (test_data_dir)) + die (test_name); + } + + check_memleaks (); +} + +#endif /* DBUS_BUILD_TESTS */ + +/** + * An exported symbol to be run in order to execute + * unit tests. Should not be used by + * any app other than our test app, this symbol + * won't exist in some builds of the library. + * (with --enable-tests=no) + * + * @param test_data_dir the directory with test data (test/data normally) + */ +void +dbus_internal_do_not_use_run_tests (const char *test_data_dir, const char *specific_test) +{ +#ifdef DBUS_BUILD_TESTS + if (!_dbus_threads_init_debug ()) + die ("debug threads init"); + + if (test_data_dir == NULL) + test_data_dir = _dbus_getenv ("DBUS_TEST_DATA"); + + if (test_data_dir != NULL) + printf ("Test data in %s\n", test_data_dir); + else + printf ("No test data!\n"); + + run_test ("string", specific_test, _dbus_string_test); + + run_test ("sysdeps", specific_test, _dbus_sysdeps_test); + + run_test ("data-slot", specific_test, _dbus_data_slot_test); + + run_test ("misc", specific_test, _dbus_misc_test); + + run_test ("address", specific_test, _dbus_address_test); + + run_test ("server", specific_test, _dbus_server_test); + + run_test ("object-tree", specific_test, _dbus_object_tree_test); + + run_test ("signature", specific_test, _dbus_signature_test); + + run_test ("marshalling", specific_test, _dbus_marshal_test); + +#if 0 + printf ("%s: running recursive marshalling tests\n", "dbus-test"); + if (!_dbus_marshal_recursive_test ()) + die ("recursive marshal"); + + check_memleaks (); +#else + _dbus_warn ("recursive marshal tests disabled\n"); +#endif + + run_test ("byteswap", specific_test, _dbus_marshal_byteswap_test); + + run_test ("memory", specific_test, _dbus_memory_test); + +#if 1 + run_test ("mem-pool", specific_test, _dbus_mem_pool_test); +#endif + + run_test ("list", specific_test, _dbus_list_test); + + run_test ("marshal-validate", specific_test, _dbus_marshal_validate_test); + + run_test ("marshal-header", specific_test, _dbus_marshal_header_test); + + run_data_test ("message", specific_test, _dbus_message_test, test_data_dir); + + run_test ("hash", specific_test, _dbus_hash_test); + +#if !defined(DBUS_WINCE) + run_data_test ("spawn", specific_test, _dbus_spawn_test, test_data_dir); +#endif + + run_data_test ("credentials", specific_test, _dbus_credentials_test, test_data_dir); + +#ifdef DBUS_UNIX + run_data_test ("userdb", specific_test, _dbus_userdb_test, test_data_dir); +#endif + + run_test ("keyring", specific_test, _dbus_keyring_test); + +#if 0 + printf ("%s: running md5 tests\n", "dbus-test"); + if (!_dbus_md5_test ()) + die ("md5"); + + check_memleaks (); +#endif + + run_data_test ("sha", specific_test, _dbus_sha_test, test_data_dir); + + run_data_test ("auth", specific_test, _dbus_auth_test, test_data_dir); + + run_data_test ("pending-call", specific_test, _dbus_pending_call_test, test_data_dir); + + printf ("%s: completed successfully\n", "dbus-test"); +#else + printf ("Not compiled with unit tests, not running any\n"); +#endif +} + diff --git a/src/dbus/dbus-test.h b/src/dbus/dbus-test.h new file mode 100644 index 0000000..a7aaea6 --- /dev/null +++ b/src/dbus/dbus-test.h @@ -0,0 +1,83 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-test.h Declarations of test functions. + * + * Copyright (C) 2002 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef DBUS_TEST_H +#define DBUS_TEST_H + +#include +#include +#include + +dbus_bool_t _dbus_hash_test (void); +dbus_bool_t _dbus_dict_test (void); +dbus_bool_t _dbus_list_test (void); +dbus_bool_t _dbus_marshal_test (void); +dbus_bool_t _dbus_marshal_recursive_test (void); +dbus_bool_t _dbus_marshal_byteswap_test (void); +dbus_bool_t _dbus_marshal_header_test (void); +dbus_bool_t _dbus_marshal_validate_test (void); +dbus_bool_t _dbus_misc_test (void); +dbus_bool_t _dbus_signature_test (void); +dbus_bool_t _dbus_mem_pool_test (void); +dbus_bool_t _dbus_string_test (void); +dbus_bool_t _dbus_address_test (void); +dbus_bool_t _dbus_server_test (void); +dbus_bool_t _dbus_message_test (const char *test_data_dir); +dbus_bool_t _dbus_auth_test (const char *test_data_dir); +dbus_bool_t _dbus_md5_test (void); +dbus_bool_t _dbus_sha_test (const char *test_data_dir); +dbus_bool_t _dbus_keyring_test (void); +dbus_bool_t _dbus_data_slot_test (void); +dbus_bool_t _dbus_sysdeps_test (void); +dbus_bool_t _dbus_spawn_test (const char *test_data_dir); +dbus_bool_t _dbus_userdb_test (const char *test_data_dir); +dbus_bool_t _dbus_memory_test (void); +dbus_bool_t _dbus_object_tree_test (void); +dbus_bool_t _dbus_pending_call_test (const char *test_data_dir); +dbus_bool_t _dbus_credentials_test (const char *test_data_dir); + +void dbus_internal_do_not_use_run_tests (const char *test_data_dir, + const char *specific_test); +dbus_bool_t dbus_internal_do_not_use_try_message_file (const DBusString *filename, + DBusValidity expected_validity); +dbus_bool_t dbus_internal_do_not_use_try_message_data (const DBusString *data, + DBusValidity expected_validity); +dbus_bool_t dbus_internal_do_not_use_load_message_file (const DBusString *filename, + DBusString *data); + + +/* returns FALSE on fatal failure */ +typedef dbus_bool_t (* DBusForeachMessageFileFunc) (const DBusString *filename, + DBusValidity expected_validity, + void *data); + +dbus_bool_t dbus_internal_do_not_use_foreach_message_file (const char *test_data_dir, + DBusForeachMessageFileFunc func, + void *user_data); +dbus_bool_t dbus_internal_do_not_use_generate_bodies (int sequence, + int byte_order, + DBusString *signature, + DBusString *body); + + +#endif /* DBUS_TEST_H */ diff --git a/src/dbus/dbus-threads-internal.h b/src/dbus/dbus-threads-internal.h new file mode 100644 index 0000000..bcc4e6b --- /dev/null +++ b/src/dbus/dbus-threads-internal.h @@ -0,0 +1,53 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-threads-internal.h D-Bus thread primitives + * + * Copyright (C) 2002, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef DBUS_THREADS_INTERNAL_H +#define DBUS_THREADS_INTERNAL_H + +#include +#include +#include + +DBUS_BEGIN_DECLS + +DBusMutex* _dbus_mutex_new (void); +void _dbus_mutex_free (DBusMutex *mutex); +void _dbus_mutex_lock (DBusMutex *mutex); +void _dbus_mutex_unlock (DBusMutex *mutex); +void _dbus_mutex_new_at_location (DBusMutex **location_p); +void _dbus_mutex_free_at_location (DBusMutex **location_p); + +DBusCondVar* _dbus_condvar_new (void); +void _dbus_condvar_free (DBusCondVar *cond); +void _dbus_condvar_wait (DBusCondVar *cond, + DBusMutex *mutex); +dbus_bool_t _dbus_condvar_wait_timeout (DBusCondVar *cond, + DBusMutex *mutex, + int timeout_milliseconds); +void _dbus_condvar_wake_one (DBusCondVar *cond); +void _dbus_condvar_wake_all (DBusCondVar *cond); +void _dbus_condvar_new_at_location (DBusCondVar **location_p); +void _dbus_condvar_free_at_location (DBusCondVar **location_p); + +DBUS_END_DECLS + +#endif /* DBUS_THREADS_INTERNAL_H */ diff --git a/src/dbus/dbus-threads.c b/src/dbus/dbus-threads.c new file mode 100644 index 0000000..00c1a4b --- /dev/null +++ b/src/dbus/dbus-threads.c @@ -0,0 +1,816 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-threads.h D-Bus threads handling + * + * Copyright (C) 2002, 2003, 2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include "dbus-threads.h" +#include "dbus-internals.h" +#include "dbus-threads-internal.h" +#include "dbus-list.h" + +static DBusThreadFunctions thread_functions = +{ + 0, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + + NULL, NULL, NULL, NULL +}; + +static int thread_init_generation = 0; + +static DBusList *uninitialized_mutex_list = NULL; +static DBusList *uninitialized_condvar_list = NULL; + +/** This is used for the no-op default mutex pointer, just to be distinct from #NULL */ +#define _DBUS_DUMMY_MUTEX ((DBusMutex*)0xABCDEF) + +/** This is used for the no-op default mutex pointer, just to be distinct from #NULL */ +#define _DBUS_DUMMY_CONDVAR ((DBusCondVar*)0xABCDEF2) + +/** + * @defgroup DBusThreadsInternals Thread functions + * @ingroup DBusInternals + * @brief _dbus_mutex_lock(), etc. + * + * Functions and macros related to threads and thread locks. + * + * @{ + */ + +/** + * Creates a new mutex using the function supplied to dbus_threads_init(), + * or creates a no-op mutex if threads are not initialized. + * May return #NULL even if threads are initialized, indicating + * out-of-memory. + * + * @returns new mutex or #NULL + */ +DBusMutex* +_dbus_mutex_new (void) +{ + if (thread_functions.recursive_mutex_new) + return (* thread_functions.recursive_mutex_new) (); + else if (thread_functions.mutex_new) + return (* thread_functions.mutex_new) (); + else + return _DBUS_DUMMY_MUTEX; +} + +/** + * This does the same thing as _dbus_mutex_new. It however + * gives another level of indirection by allocating a pointer + * to point to the mutex location. This allows the threading + * module to swap out dummy mutexes for real a real mutex so libraries + * can initialize threads even after the D-Bus API has been used. + * + * @param location_p the location of the new mutex, can return #NULL on OOM + */ +void +_dbus_mutex_new_at_location (DBusMutex **location_p) +{ + _dbus_assert (location_p != NULL); + + *location_p = _dbus_mutex_new(); + + if (thread_init_generation != _dbus_current_generation && *location_p) + { + if (!_dbus_list_append (&uninitialized_mutex_list, location_p)) + { + _dbus_mutex_free (*location_p); + *location_p = NULL; + } + } +} + +/** + * Frees a mutex created with dbus_mutex_new(); does + * nothing if passed a #NULL pointer. + */ +void +_dbus_mutex_free (DBusMutex *mutex) +{ + if (mutex) + { + if (mutex && thread_functions.recursive_mutex_free) + (* thread_functions.recursive_mutex_free) (mutex); + else if (mutex && thread_functions.mutex_free) + (* thread_functions.mutex_free) (mutex); + } +} + +/** + * Frees a mutex and removes it from the + * uninitialized_mutex_list; + * does nothing if passed a #NULL pointer. + */ +void +_dbus_mutex_free_at_location (DBusMutex **location_p) +{ + if (location_p) + { + if (thread_init_generation != _dbus_current_generation) + _dbus_list_remove (&uninitialized_mutex_list, location_p); + + _dbus_mutex_free (*location_p); + } +} + +/** + * Locks a mutex. Does nothing if passed a #NULL pointer. + * Locks may be recursive if threading implementation initialized + * recursive locks. + */ +void +_dbus_mutex_lock (DBusMutex *mutex) +{ + if (mutex) + { + if (thread_functions.recursive_mutex_lock) + (* thread_functions.recursive_mutex_lock) (mutex); + else if (thread_functions.mutex_lock) + (* thread_functions.mutex_lock) (mutex); + } +} + +/** + * Unlocks a mutex. Does nothing if passed a #NULL pointer. + * + * @returns #TRUE on success + */ +void +_dbus_mutex_unlock (DBusMutex *mutex) +{ + if (mutex) + { + if (thread_functions.recursive_mutex_unlock) + (* thread_functions.recursive_mutex_unlock) (mutex); + else if (thread_functions.mutex_unlock) + (* thread_functions.mutex_unlock) (mutex); + } +} + +/** + * Creates a new condition variable using the function supplied + * to dbus_threads_init(), or creates a no-op condition variable + * if threads are not initialized. May return #NULL even if + * threads are initialized, indicating out-of-memory. + * + * @returns new mutex or #NULL + */ +DBusCondVar * +_dbus_condvar_new (void) +{ + if (thread_functions.condvar_new) + return (* thread_functions.condvar_new) (); + else + return _DBUS_DUMMY_CONDVAR; +} + + +/** + * This does the same thing as _dbus_condvar_new. It however + * gives another level of indirection by allocating a pointer + * to point to the condvar location. This allows the threading + * module to swap out dummy condvars for real a real condvar so libraries + * can initialize threads even after the D-Bus API has been used. + * + * @returns the location of a new condvar or #NULL on OOM + */ + +void +_dbus_condvar_new_at_location (DBusCondVar **location_p) +{ + *location_p = _dbus_condvar_new(); + + if (thread_init_generation != _dbus_current_generation && *location_p) + { + if (!_dbus_list_append (&uninitialized_condvar_list, location_p)) + { + _dbus_condvar_free (*location_p); + *location_p = NULL; + } + } +} + + +/** + * Frees a conditional variable created with dbus_condvar_new(); does + * nothing if passed a #NULL pointer. + */ +void +_dbus_condvar_free (DBusCondVar *cond) +{ + if (cond && thread_functions.condvar_free) + (* thread_functions.condvar_free) (cond); +} + +/** + * Frees a conditional variable and removes it from the + * uninitialized_condvar_list; + * does nothing if passed a #NULL pointer. + */ +void +_dbus_condvar_free_at_location (DBusCondVar **location_p) +{ + if (location_p) + { + if (thread_init_generation != _dbus_current_generation) + _dbus_list_remove (&uninitialized_condvar_list, location_p); + + _dbus_condvar_free (*location_p); + } +} + +/** + * Atomically unlocks the mutex and waits for the conditions + * variable to be signalled. Locks the mutex again before + * returning. + * Does nothing if passed a #NULL pointer. + */ +void +_dbus_condvar_wait (DBusCondVar *cond, + DBusMutex *mutex) +{ + if (cond && mutex && thread_functions.condvar_wait) + (* thread_functions.condvar_wait) (cond, mutex); +} + +/** + * Atomically unlocks the mutex and waits for the conditions variable + * to be signalled, or for a timeout. Locks the mutex again before + * returning. Does nothing if passed a #NULL pointer. Return value + * is #FALSE if we timed out, #TRUE otherwise. + * + * @param cond the condition variable + * @param mutex the mutex + * @param timeout_milliseconds the maximum time to wait + * @returns #FALSE if the timeout occurred, #TRUE if not + */ +dbus_bool_t +_dbus_condvar_wait_timeout (DBusCondVar *cond, + DBusMutex *mutex, + int timeout_milliseconds) +{ + if (cond && mutex && thread_functions.condvar_wait) + return (* thread_functions.condvar_wait_timeout) (cond, mutex, timeout_milliseconds); + else + return TRUE; +} + +/** + * If there are threads waiting on the condition variable, wake + * up exactly one. + * Does nothing if passed a #NULL pointer. + */ +void +_dbus_condvar_wake_one (DBusCondVar *cond) +{ + if (cond && thread_functions.condvar_wake_one) + (* thread_functions.condvar_wake_one) (cond); +} + +/** + * If there are threads waiting on the condition variable, wake + * up all of them. + * Does nothing if passed a #NULL pointer. + */ +void +_dbus_condvar_wake_all (DBusCondVar *cond) +{ + if (cond && thread_functions.condvar_wake_all) + (* thread_functions.condvar_wake_all) (cond); +} + +static void +shutdown_global_locks (void *data) +{ + DBusMutex ***locks = data; + int i; + + i = 0; + while (i < _DBUS_N_GLOBAL_LOCKS) + { + _dbus_mutex_free (*(locks[i])); + *(locks[i]) = NULL; + ++i; + } + + dbus_free (locks); +} + +static void +shutdown_uninitialized_locks (void *data) +{ + _dbus_list_clear (&uninitialized_mutex_list); + _dbus_list_clear (&uninitialized_condvar_list); +} + +static dbus_bool_t +init_uninitialized_locks (void) +{ + DBusList *link; + + _dbus_assert (thread_init_generation != _dbus_current_generation); + + link = uninitialized_mutex_list; + while (link != NULL) + { + DBusMutex **mp; + + mp = (DBusMutex **)link->data; + _dbus_assert (*mp == _DBUS_DUMMY_MUTEX); + + *mp = _dbus_mutex_new (); + if (*mp == NULL) + goto fail_mutex; + + link = _dbus_list_get_next_link (&uninitialized_mutex_list, link); + } + + link = uninitialized_condvar_list; + while (link != NULL) + { + DBusCondVar **cp; + + cp = (DBusCondVar **)link->data; + _dbus_assert (*cp == _DBUS_DUMMY_CONDVAR); + + *cp = _dbus_condvar_new (); + if (*cp == NULL) + goto fail_condvar; + + link = _dbus_list_get_next_link (&uninitialized_condvar_list, link); + } + + _dbus_list_clear (&uninitialized_mutex_list); + _dbus_list_clear (&uninitialized_condvar_list); + + if (!_dbus_register_shutdown_func (shutdown_uninitialized_locks, + NULL)) + goto fail_condvar; + + return TRUE; + + fail_condvar: + link = uninitialized_condvar_list; + while (link != NULL) + { + DBusCondVar **cp; + + cp = (DBusCondVar **)link->data; + + if (*cp != _DBUS_DUMMY_CONDVAR) + _dbus_condvar_free (*cp); + else + break; + + *cp = _DBUS_DUMMY_CONDVAR; + + link = _dbus_list_get_next_link (&uninitialized_condvar_list, link); + } + + fail_mutex: + link = uninitialized_mutex_list; + while (link != NULL) + { + DBusMutex **mp; + + mp = (DBusMutex **)link->data; + + if (*mp != _DBUS_DUMMY_MUTEX) + _dbus_mutex_free (*mp); + else + break; + + *mp = _DBUS_DUMMY_MUTEX; + + link = _dbus_list_get_next_link (&uninitialized_mutex_list, link); + } + + return FALSE; +} + +static dbus_bool_t +init_locks (void) +{ + int i; + DBusMutex ***dynamic_global_locks; + + DBusMutex **global_locks[] = { +#define LOCK_ADDR(name) (& _dbus_lock_##name) + LOCK_ADDR (win_fds), + LOCK_ADDR (sid_atom_cache), + LOCK_ADDR (list), + LOCK_ADDR (connection_slots), + LOCK_ADDR (pending_call_slots), + LOCK_ADDR (server_slots), + LOCK_ADDR (message_slots), + LOCK_ADDR (atomic), + LOCK_ADDR (bus), + LOCK_ADDR (bus_datas), + LOCK_ADDR (shutdown_funcs), + LOCK_ADDR (system_users), + LOCK_ADDR (message_cache), + LOCK_ADDR (shared_connections), + LOCK_ADDR (machine_uuid) +#undef LOCK_ADDR + }; + + _dbus_assert (_DBUS_N_ELEMENTS (global_locks) == + _DBUS_N_GLOBAL_LOCKS); + + i = 0; + + dynamic_global_locks = dbus_new (DBusMutex**, _DBUS_N_GLOBAL_LOCKS); + if (dynamic_global_locks == NULL) + goto failed; + + while (i < _DBUS_N_ELEMENTS (global_locks)) + { + *global_locks[i] = _dbus_mutex_new (); + + if (*global_locks[i] == NULL) + goto failed; + + dynamic_global_locks[i] = global_locks[i]; + + ++i; + } + + if (!_dbus_register_shutdown_func (shutdown_global_locks, + dynamic_global_locks)) + goto failed; + + if (!init_uninitialized_locks ()) + goto failed; + + return TRUE; + + failed: + dbus_free (dynamic_global_locks); + + for (i = i - 1; i >= 0; i--) + { + _dbus_mutex_free (*global_locks[i]); + *global_locks[i] = NULL; + } + return FALSE; +} + +/** @} */ /* end of internals */ + +/** + * @defgroup DBusThreads Thread functions + * @ingroup DBus + * @brief dbus_threads_init() and dbus_threads_init_default() + * + * Functions and macros related to threads and thread locks. + * + * If threads are initialized, the D-Bus library has locks on all + * global data structures. In addition, each #DBusConnection has a + * lock, so only one thread at a time can touch the connection. (See + * @ref DBusConnection for more on connection locking.) + * + * Most other objects, however, do not have locks - they can only be + * used from a single thread at a time, unless you lock them yourself. + * For example, a #DBusMessage can't be modified from two threads + * at once. + * + * @{ + */ + +/** + * + * Initializes threads. If this function is not called, the D-Bus + * library will not lock any data structures. If it is called, D-Bus + * will do locking, at some cost in efficiency. Note that this + * function must be called BEFORE the second thread is started. + * + * Almost always, you should use dbus_threads_init_default() instead. + * The raw dbus_threads_init() is only useful if you require a + * particular thread implementation for some reason. + * + * A possible reason to use dbus_threads_init() rather than + * dbus_threads_init_default() is to insert debugging checks or print + * statements. + * + * dbus_threads_init() may be called more than once. The first one + * wins and subsequent calls are ignored. (Unless you use + * dbus_shutdown() to reset libdbus, which will let you re-init + * threads.) + * + * Either recursive or nonrecursive mutex functions must be specified, + * but not both. New code should provide only the recursive functions + * - specifying the nonrecursive ones is deprecated. + * + * Because this function effectively sets global state, all code + * running in a given application must agree on the thread + * implementation. Most code won't care which thread implementation is + * used, so there's no problem. However, usually libraries should not + * call dbus_threads_init() or dbus_threads_init_default(), instead + * leaving this policy choice to applications. + * + * The exception is for application frameworks (GLib, Qt, etc.) and + * D-Bus bindings based on application frameworks. These frameworks + * define a cross-platform thread abstraction and can assume + * applications using the framework are OK with using that thread + * abstraction. + * + * However, even these app frameworks may find it easier to simply call + * dbus_threads_init_default(), and there's no reason they shouldn't. + * + * @param functions functions for using threads + * @returns #TRUE on success, #FALSE if no memory + */ +dbus_bool_t +dbus_threads_init (const DBusThreadFunctions *functions) +{ + dbus_bool_t mutex_set; + dbus_bool_t recursive_mutex_set; + + _dbus_assert (functions != NULL); + + /* these base functions are required. Future additions to + * DBusThreadFunctions may be optional. + */ + _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK); + _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK); + _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK); + _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK); + _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK); + _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK); + _dbus_assert (functions->condvar_new != NULL); + _dbus_assert (functions->condvar_free != NULL); + _dbus_assert (functions->condvar_wait != NULL); + _dbus_assert (functions->condvar_wait_timeout != NULL); + _dbus_assert (functions->condvar_wake_one != NULL); + _dbus_assert (functions->condvar_wake_all != NULL); + + /* Either the mutex function set or recursive mutex set needs + * to be available but not both + */ + mutex_set = (functions->mask & DBUS_THREAD_FUNCTIONS_MUTEX_NEW_MASK) && + (functions->mask & DBUS_THREAD_FUNCTIONS_MUTEX_FREE_MASK) && + (functions->mask & DBUS_THREAD_FUNCTIONS_MUTEX_LOCK_MASK) && + (functions->mask & DBUS_THREAD_FUNCTIONS_MUTEX_UNLOCK_MASK) && + functions->mutex_new && + functions->mutex_free && + functions->mutex_lock && + functions->mutex_unlock; + + recursive_mutex_set = + (functions->mask & DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_NEW_MASK) && + (functions->mask & DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_FREE_MASK) && + (functions->mask & DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_LOCK_MASK) && + (functions->mask & DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_UNLOCK_MASK) && + functions->recursive_mutex_new && + functions->recursive_mutex_free && + functions->recursive_mutex_lock && + functions->recursive_mutex_unlock; + + if (!(mutex_set || recursive_mutex_set)) + _dbus_assert_not_reached ("Either the nonrecusrive or recursive mutex " + "functions sets should be passed into " + "dbus_threads_init. Neither sets were passed."); + + if (mutex_set && recursive_mutex_set) + _dbus_assert_not_reached ("Either the nonrecusrive or recursive mutex " + "functions sets should be passed into " + "dbus_threads_init. Both sets were passed. " + "You most likely just want to set the recursive " + "mutex functions to avoid deadlocks in D-Bus."); + + /* Check that all bits in the mask actually are valid mask bits. + * ensures people won't write code that breaks when we add + * new bits. + */ + _dbus_assert ((functions->mask & ~DBUS_THREAD_FUNCTIONS_ALL_MASK) == 0); + + if (thread_init_generation != _dbus_current_generation) + thread_functions.mask = 0; /* allow re-init in new generation */ + + /* Silently allow multiple init + * First init wins and D-Bus will always use its threading system + */ + if (thread_functions.mask != 0) + return TRUE; + + thread_functions.mutex_new = functions->mutex_new; + thread_functions.mutex_free = functions->mutex_free; + thread_functions.mutex_lock = functions->mutex_lock; + thread_functions.mutex_unlock = functions->mutex_unlock; + + thread_functions.condvar_new = functions->condvar_new; + thread_functions.condvar_free = functions->condvar_free; + thread_functions.condvar_wait = functions->condvar_wait; + thread_functions.condvar_wait_timeout = functions->condvar_wait_timeout; + thread_functions.condvar_wake_one = functions->condvar_wake_one; + thread_functions.condvar_wake_all = functions->condvar_wake_all; + + if (functions->mask & DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_NEW_MASK) + thread_functions.recursive_mutex_new = functions->recursive_mutex_new; + + if (functions->mask & DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_FREE_MASK) + thread_functions.recursive_mutex_free = functions->recursive_mutex_free; + + if (functions->mask & DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_LOCK_MASK) + thread_functions.recursive_mutex_lock = functions->recursive_mutex_lock; + + if (functions->mask & DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_UNLOCK_MASK) + thread_functions.recursive_mutex_unlock = functions->recursive_mutex_unlock; + + thread_functions.mask = functions->mask; + + if (!init_locks ()) + return FALSE; + + thread_init_generation = _dbus_current_generation; + + return TRUE; +} + + + +/* Default thread implemenation */ + +/** + * + * Calls dbus_threads_init() with a default set of + * #DBusThreadFunctions appropriate for the platform. + * + * Most applications should use this rather than dbus_threads_init(). + * + * It's safe to call dbus_threads_init_default() as many times as you + * want, but only the first time will have an effect. + * + * dbus_shutdown() reverses the effects of this function when it + * resets all global state in libdbus. + * + * @returns #TRUE on success, #FALSE if not enough memory + */ +dbus_bool_t +dbus_threads_init_default (void) +{ + return _dbus_threads_init_platform_specific (); +} + + +/** @} */ + +#ifdef DBUS_BUILD_TESTS +/** Fake mutex used for debugging */ +typedef struct DBusFakeMutex DBusFakeMutex; +/** Fake mutex used for debugging */ +struct DBusFakeMutex +{ + dbus_bool_t locked; /**< Mutex is "locked" */ +}; + +static DBusMutex * dbus_fake_mutex_new (void); +static void dbus_fake_mutex_free (DBusMutex *mutex); +static dbus_bool_t dbus_fake_mutex_lock (DBusMutex *mutex); +static dbus_bool_t dbus_fake_mutex_unlock (DBusMutex *mutex); +static DBusCondVar* dbus_fake_condvar_new (void); +static void dbus_fake_condvar_free (DBusCondVar *cond); +static void dbus_fake_condvar_wait (DBusCondVar *cond, + DBusMutex *mutex); +static dbus_bool_t dbus_fake_condvar_wait_timeout (DBusCondVar *cond, + DBusMutex *mutex, + int timeout_msec); +static void dbus_fake_condvar_wake_one (DBusCondVar *cond); +static void dbus_fake_condvar_wake_all (DBusCondVar *cond); + + +static const DBusThreadFunctions fake_functions = +{ + DBUS_THREAD_FUNCTIONS_MUTEX_NEW_MASK | + DBUS_THREAD_FUNCTIONS_MUTEX_FREE_MASK | + DBUS_THREAD_FUNCTIONS_MUTEX_LOCK_MASK | + DBUS_THREAD_FUNCTIONS_MUTEX_UNLOCK_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK| + DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK, + dbus_fake_mutex_new, + dbus_fake_mutex_free, + dbus_fake_mutex_lock, + dbus_fake_mutex_unlock, + dbus_fake_condvar_new, + dbus_fake_condvar_free, + dbus_fake_condvar_wait, + dbus_fake_condvar_wait_timeout, + dbus_fake_condvar_wake_one, + dbus_fake_condvar_wake_all +}; + +static DBusMutex * +dbus_fake_mutex_new (void) +{ + DBusFakeMutex *mutex; + + mutex = dbus_new0 (DBusFakeMutex, 1); + + return (DBusMutex *)mutex; +} + +static void +dbus_fake_mutex_free (DBusMutex *mutex) +{ + DBusFakeMutex *fake = (DBusFakeMutex*) mutex; + + _dbus_assert (!fake->locked); + + dbus_free (fake); +} + +static dbus_bool_t +dbus_fake_mutex_lock (DBusMutex *mutex) +{ + DBusFakeMutex *fake = (DBusFakeMutex*) mutex; + + _dbus_assert (!fake->locked); + + fake->locked = TRUE; + + return TRUE; +} + +static dbus_bool_t +dbus_fake_mutex_unlock (DBusMutex *mutex) +{ + DBusFakeMutex *fake = (DBusFakeMutex*) mutex; + + _dbus_assert (fake->locked); + + fake->locked = FALSE; + + return TRUE; +} + +static DBusCondVar* +dbus_fake_condvar_new (void) +{ + return (DBusCondVar*) _dbus_strdup ("FakeCondvar"); +} + +static void +dbus_fake_condvar_free (DBusCondVar *cond) +{ + dbus_free (cond); +} + +static void +dbus_fake_condvar_wait (DBusCondVar *cond, + DBusMutex *mutex) +{ + +} + +static dbus_bool_t +dbus_fake_condvar_wait_timeout (DBusCondVar *cond, + DBusMutex *mutex, + int timeout_msec) +{ + return TRUE; +} + +static void +dbus_fake_condvar_wake_one (DBusCondVar *cond) +{ + +} + +static void +dbus_fake_condvar_wake_all (DBusCondVar *cond) +{ + +} + +dbus_bool_t +_dbus_threads_init_debug (void) +{ + return dbus_threads_init (&fake_functions); +} + +#endif /* DBUS_BUILD_TESTS */ diff --git a/src/dbus/dbus-threads.h b/src/dbus/dbus-threads.h new file mode 100644 index 0000000..8d9687d --- /dev/null +++ b/src/dbus/dbus-threads.h @@ -0,0 +1,196 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-threads.h D-Bus threads handling + * + * Copyright (C) 2002 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION) +#error "Only can be included directly, this file may disappear or change contents." +#endif + +#ifndef DBUS_THREADS_H +#define DBUS_THREADS_H + +#include +#include + +DBUS_BEGIN_DECLS + +/** + * @addtogroup DBusThreads + * @{ + */ + +/** An opaque mutex type provided by the #DBusThreadFunctions implementation installed by dbus_threads_init(). */ +typedef struct DBusMutex DBusMutex; +/** An opaque condition variable type provided by the #DBusThreadFunctions implementation installed by dbus_threads_init(). */ +typedef struct DBusCondVar DBusCondVar; + +/** Deprecated, provide DBusRecursiveMutexNewFunction instead. */ +typedef DBusMutex* (* DBusMutexNewFunction) (void); +/** Deprecated, provide DBusRecursiveMutexFreeFunction instead. */ +typedef void (* DBusMutexFreeFunction) (DBusMutex *mutex); +/** Deprecated, provide DBusRecursiveMutexLockFunction instead. Return value is lock success, but gets ignored in practice. */ +typedef dbus_bool_t (* DBusMutexLockFunction) (DBusMutex *mutex); +/** Deprecated, provide DBusRecursiveMutexUnlockFunction instead. Return value is unlock success, but gets ignored in practice. */ +typedef dbus_bool_t (* DBusMutexUnlockFunction) (DBusMutex *mutex); + +/** Creates a new recursively-lockable mutex, or returns #NULL if not + * enough memory. Can only fail due to lack of memory. Found in + * #DBusThreadFunctions. Do not just use PTHREAD_MUTEX_RECURSIVE for + * this, because it does not save/restore the recursion count when + * waiting on a condition. libdbus requires the Java-style behavior + * where the mutex is fully unlocked to wait on a condition. + */ +typedef DBusMutex* (* DBusRecursiveMutexNewFunction) (void); +/** Frees a recursively-lockable mutex. Found in #DBusThreadFunctions. + */ +typedef void (* DBusRecursiveMutexFreeFunction) (DBusMutex *mutex); +/** Locks a recursively-lockable mutex. Found in #DBusThreadFunctions. + * Can only fail due to lack of memory. + */ +typedef void (* DBusRecursiveMutexLockFunction) (DBusMutex *mutex); +/** Unlocks a recursively-lockable mutex. Found in #DBusThreadFunctions. + * Can only fail due to lack of memory. + */ +typedef void (* DBusRecursiveMutexUnlockFunction) (DBusMutex *mutex); + +/** Creates a new condition variable. Found in #DBusThreadFunctions. + * Can only fail (returning #NULL) due to lack of memory. + */ +typedef DBusCondVar* (* DBusCondVarNewFunction) (void); +/** Frees a condition variable. Found in #DBusThreadFunctions. + */ +typedef void (* DBusCondVarFreeFunction) (DBusCondVar *cond); + +/** Waits on a condition variable. Found in + * #DBusThreadFunctions. Must work with either a recursive or + * nonrecursive mutex, whichever the thread implementation + * provides. Note that PTHREAD_MUTEX_RECURSIVE does not work with + * condition variables (does not save/restore the recursion count) so + * don't try using simply pthread_cond_wait() and a + * PTHREAD_MUTEX_RECURSIVE to implement this, it won't work right. + * + * Has no error conditions. Must succeed if it returns. + */ +typedef void (* DBusCondVarWaitFunction) (DBusCondVar *cond, + DBusMutex *mutex); + +/** Waits on a condition variable with a timeout. Found in + * #DBusThreadFunctions. Returns #TRUE if the wait did not + * time out, and #FALSE if it did. + * + * Has no error conditions. Must succeed if it returns. + */ +typedef dbus_bool_t (* DBusCondVarWaitTimeoutFunction) (DBusCondVar *cond, + DBusMutex *mutex, + int timeout_milliseconds); +/** Wakes one waiting thread on a condition variable. Found in #DBusThreadFunctions. + * + * Has no error conditions. Must succeed if it returns. + */ +typedef void (* DBusCondVarWakeOneFunction) (DBusCondVar *cond); + +/** Wakes all waiting threads on a condition variable. Found in #DBusThreadFunctions. + * + * Has no error conditions. Must succeed if it returns. + */ +typedef void (* DBusCondVarWakeAllFunction) (DBusCondVar *cond); + +/** + * Flags indicating which functions are present in #DBusThreadFunctions. Used to allow + * the library to detect older callers of dbus_threads_init() if new possible functions + * are added to #DBusThreadFunctions. + */ +typedef enum +{ + DBUS_THREAD_FUNCTIONS_MUTEX_NEW_MASK = 1 << 0, + DBUS_THREAD_FUNCTIONS_MUTEX_FREE_MASK = 1 << 1, + DBUS_THREAD_FUNCTIONS_MUTEX_LOCK_MASK = 1 << 2, + DBUS_THREAD_FUNCTIONS_MUTEX_UNLOCK_MASK = 1 << 3, + DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK = 1 << 4, + DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK = 1 << 5, + DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK = 1 << 6, + DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK = 1 << 7, + DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK = 1 << 8, + DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK = 1 << 9, + DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_NEW_MASK = 1 << 10, + DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_FREE_MASK = 1 << 11, + DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_LOCK_MASK = 1 << 12, + DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_UNLOCK_MASK = 1 << 13, + DBUS_THREAD_FUNCTIONS_ALL_MASK = (1 << 14) - 1 +} DBusThreadFunctionsMask; + +/** + * Functions that must be implemented to make the D-Bus library + * thread-aware. The recursive mutex functions should be specified + * rather than the old, deprecated nonrecursive ones. + * + * The condition variable functions have to work with recursive + * mutexes if you provide those, or with nonrecursive mutexes if you + * provide those. + * + * If implementing threads using pthreads, be aware that + * PTHREAD_MUTEX_RECURSIVE is broken in combination with condition + * variables. libdbus relies on the Java-style behavior that when + * waiting on a condition, the recursion count is saved and restored, + * and the mutex is completely unlocked, not just decremented one + * level of recursion. + * + * Thus with pthreads you probably have to roll your own emulated + * recursive mutexes, you can't use PTHREAD_MUTEX_RECURSIVE. This is + * what dbus_threads_init_default() does on platforms that use + * pthreads. + */ +typedef struct +{ + unsigned int mask; /**< Mask indicating which functions are present. */ + + DBusMutexNewFunction mutex_new; /**< Function to create a mutex; optional and deprecated. */ + DBusMutexFreeFunction mutex_free; /**< Function to free a mutex; optional and deprecated. */ + DBusMutexLockFunction mutex_lock; /**< Function to lock a mutex; optional and deprecated. */ + DBusMutexUnlockFunction mutex_unlock; /**< Function to unlock a mutex; optional and deprecated. */ + + DBusCondVarNewFunction condvar_new; /**< Function to create a condition variable */ + DBusCondVarFreeFunction condvar_free; /**< Function to free a condition variable */ + DBusCondVarWaitFunction condvar_wait; /**< Function to wait on a condition */ + DBusCondVarWaitTimeoutFunction condvar_wait_timeout; /**< Function to wait on a condition with a timeout */ + DBusCondVarWakeOneFunction condvar_wake_one; /**< Function to wake one thread waiting on the condition */ + DBusCondVarWakeAllFunction condvar_wake_all; /**< Function to wake all threads waiting on the condition */ + + DBusRecursiveMutexNewFunction recursive_mutex_new; /**< Function to create a recursive mutex */ + DBusRecursiveMutexFreeFunction recursive_mutex_free; /**< Function to free a recursive mutex */ + DBusRecursiveMutexLockFunction recursive_mutex_lock; /**< Function to lock a recursive mutex */ + DBusRecursiveMutexUnlockFunction recursive_mutex_unlock; /**< Function to unlock a recursive mutex */ + + void (* padding1) (void); /**< Reserved for future expansion */ + void (* padding2) (void); /**< Reserved for future expansion */ + void (* padding3) (void); /**< Reserved for future expansion */ + void (* padding4) (void); /**< Reserved for future expansion */ + +} DBusThreadFunctions; + +dbus_bool_t dbus_threads_init (const DBusThreadFunctions *functions); +dbus_bool_t dbus_threads_init_default (void); + +/** @} */ + +DBUS_END_DECLS + +#endif /* DBUS_THREADS_H */ diff --git a/src/dbus/dbus-timeout.c b/src/dbus/dbus-timeout.c new file mode 100644 index 0000000..5e71c4b --- /dev/null +++ b/src/dbus/dbus-timeout.c @@ -0,0 +1,490 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-timeout.c DBusTimeout implementation + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-internals.h" +#include "dbus-timeout.h" +#include "dbus-list.h" + +/** + * @defgroup DBusTimeoutInternals DBusTimeout implementation details + * @ingroup DBusInternals + * @brief implementation details for DBusTimeout + * + * @{ + */ + +/** + * Internals of DBusTimeout + */ +struct DBusTimeout +{ + int refcount; /**< Reference count */ + int interval; /**< Timeout interval in milliseconds. */ + + DBusTimeoutHandler handler; /**< Timeout handler. */ + void *handler_data; /**< Timeout handler data. */ + DBusFreeFunction free_handler_data_function; /**< Free the timeout handler data. */ + + void *data; /**< Application data. */ + DBusFreeFunction free_data_function; /**< Free the application data. */ + unsigned int enabled : 1; /**< True if timeout is active. */ +}; + +/** + * Creates a new DBusTimeout, enabled by default. + * @param interval the timeout interval in milliseconds. + * @param handler function to call when the timeout occurs. + * @param data data to pass to the handler + * @param free_data_function function to be called to free the data. + * @returns the new DBusTimeout object, + */ +DBusTimeout* +_dbus_timeout_new (int interval, + DBusTimeoutHandler handler, + void *data, + DBusFreeFunction free_data_function) +{ + DBusTimeout *timeout; + + timeout = dbus_new0 (DBusTimeout, 1); + if (timeout == NULL) + return NULL; + + timeout->refcount = 1; + timeout->interval = interval; + + timeout->handler = handler; + timeout->handler_data = data; + timeout->free_handler_data_function = free_data_function; + + timeout->enabled = TRUE; + + return timeout; +} + +/** + * Increments the reference count of a DBusTimeout object. + * + * @param timeout the timeout object. + * @returns the timeout object. + */ +DBusTimeout * +_dbus_timeout_ref (DBusTimeout *timeout) +{ + timeout->refcount += 1; + + return timeout; +} + +/** + * Decrements the reference count of a DBusTimeout object + * and finalizes the object if the count reaches zero. + * + * @param timeout the timeout object. + */ +void +_dbus_timeout_unref (DBusTimeout *timeout) +{ + _dbus_assert (timeout != NULL); + _dbus_assert (timeout->refcount > 0); + + timeout->refcount -= 1; + if (timeout->refcount == 0) + { + dbus_timeout_set_data (timeout, NULL, NULL); /* call free_data_function */ + + if (timeout->free_handler_data_function) + (* timeout->free_handler_data_function) (timeout->handler_data); + + dbus_free (timeout); + } +} + +/** + * Changes the timeout interval. Note that you have to disable and + * re-enable the timeout using the timeout toggle function + * (_dbus_connection_toggle_timeout_unlocked() etc.) to notify the + * application of this change. + * + * @param timeout the timeout + * @param interval the new interval + */ +void +_dbus_timeout_set_interval (DBusTimeout *timeout, + int interval) +{ + _dbus_assert (interval >= 0); + + timeout->interval = interval; +} + +/** + * Changes the timeout's enabled-ness. Note that you should use + * _dbus_connection_toggle_timeout_unlocked() etc. instead, if + * the timeout is passed out to an application main loop. + * i.e. you can't use this function in the D-Bus library, it's + * only used in the message bus daemon implementation. + * + * @param timeout the timeout + * @param enabled #TRUE if timeout should be enabled. + */ +void +_dbus_timeout_set_enabled (DBusTimeout *timeout, + dbus_bool_t enabled) +{ + timeout->enabled = enabled != FALSE; +} + + +/** + * @typedef DBusTimeoutList + * + * Opaque data type representing a list of timeouts + * and a set of DBusAddTimeoutFunction/DBusRemoveTimeoutFunction. + * Automatically handles removing/re-adding timeouts + * when the DBusAddTimeoutFunction is updated or changed. + * Holds a reference count to each timeout. + * + */ + +/** + * DBusTimeoutList implementation details. All fields + * are private. + * + */ +struct DBusTimeoutList +{ + DBusList *timeouts; /**< Timeout objects. */ + + DBusAddTimeoutFunction add_timeout_function; /**< Callback for adding a timeout. */ + DBusRemoveTimeoutFunction remove_timeout_function; /**< Callback for removing a timeout. */ + DBusTimeoutToggledFunction timeout_toggled_function; /**< Callback when timeout is enabled/disabled or changes interval */ + void *timeout_data; /**< Data for timeout callbacks */ + DBusFreeFunction timeout_free_data_function; /**< Free function for timeout callback data */ +}; + +/** + * Creates a new timeout list. Returns #NULL if insufficient + * memory exists. + * + * @returns the new timeout list, or #NULL on failure. + */ +DBusTimeoutList* +_dbus_timeout_list_new (void) +{ + DBusTimeoutList *timeout_list; + + timeout_list = dbus_new0 (DBusTimeoutList, 1); + if (timeout_list == NULL) + return NULL; + + return timeout_list; +} + +/** + * Frees a DBusTimeoutList. + * + * @param timeout_list the timeout list. + */ +void +_dbus_timeout_list_free (DBusTimeoutList *timeout_list) +{ + /* free timeout_data and remove timeouts as a side effect */ + _dbus_timeout_list_set_functions (timeout_list, + NULL, NULL, NULL, NULL, NULL); + + _dbus_list_foreach (&timeout_list->timeouts, + (DBusForeachFunction) _dbus_timeout_unref, + NULL); + _dbus_list_clear (&timeout_list->timeouts); + + dbus_free (timeout_list); +} + +/** + * Sets the timeout functions. This function is the "backend" + * for dbus_connection_set_timeout_functions(). + * + * @param timeout_list the timeout list + * @param add_function the add timeout function. + * @param remove_function the remove timeout function. + * @param toggled_function toggle notify function, or #NULL + * @param data the data for those functions. + * @param free_data_function the function to free the data. + * @returns #FALSE if no memory + * + */ +dbus_bool_t +_dbus_timeout_list_set_functions (DBusTimeoutList *timeout_list, + DBusAddTimeoutFunction add_function, + DBusRemoveTimeoutFunction remove_function, + DBusTimeoutToggledFunction toggled_function, + void *data, + DBusFreeFunction free_data_function) +{ + /* Add timeouts with the new function, failing on OOM */ + if (add_function != NULL) + { + DBusList *link; + + link = _dbus_list_get_first_link (&timeout_list->timeouts); + while (link != NULL) + { + DBusList *next = _dbus_list_get_next_link (&timeout_list->timeouts, + link); + + if (!(* add_function) (link->data, data)) + { + /* remove it all again and return FALSE */ + DBusList *link2; + + link2 = _dbus_list_get_first_link (&timeout_list->timeouts); + while (link2 != link) + { + DBusList *next = _dbus_list_get_next_link (&timeout_list->timeouts, + link2); + + (* remove_function) (link2->data, data); + + link2 = next; + } + + return FALSE; + } + + link = next; + } + } + + /* Remove all current timeouts from previous timeout handlers */ + + if (timeout_list->remove_timeout_function != NULL) + { + _dbus_list_foreach (&timeout_list->timeouts, + (DBusForeachFunction) timeout_list->remove_timeout_function, + timeout_list->timeout_data); + } + + if (timeout_list->timeout_free_data_function != NULL) + (* timeout_list->timeout_free_data_function) (timeout_list->timeout_data); + + timeout_list->add_timeout_function = add_function; + timeout_list->remove_timeout_function = remove_function; + timeout_list->timeout_toggled_function = toggled_function; + timeout_list->timeout_data = data; + timeout_list->timeout_free_data_function = free_data_function; + + return TRUE; +} + +/** + * Adds a new timeout to the timeout list, invoking the + * application DBusAddTimeoutFunction if appropriate. + * + * @param timeout_list the timeout list. + * @param timeout the timeout to add. + * @returns #TRUE on success, #FALSE If no memory. + */ +dbus_bool_t +_dbus_timeout_list_add_timeout (DBusTimeoutList *timeout_list, + DBusTimeout *timeout) +{ + if (!_dbus_list_append (&timeout_list->timeouts, timeout)) + return FALSE; + + _dbus_timeout_ref (timeout); + + if (timeout_list->add_timeout_function != NULL) + { + if (!(* timeout_list->add_timeout_function) (timeout, + timeout_list->timeout_data)) + { + _dbus_list_remove_last (&timeout_list->timeouts, timeout); + _dbus_timeout_unref (timeout); + return FALSE; + } + } + + return TRUE; +} + +/** + * Removes a timeout from the timeout list, invoking the + * application's DBusRemoveTimeoutFunction if appropriate. + * + * @param timeout_list the timeout list. + * @param timeout the timeout to remove. + */ +void +_dbus_timeout_list_remove_timeout (DBusTimeoutList *timeout_list, + DBusTimeout *timeout) +{ + if (!_dbus_list_remove (&timeout_list->timeouts, timeout)) + _dbus_assert_not_reached ("Nonexistent timeout was removed"); + + if (timeout_list->remove_timeout_function != NULL) + (* timeout_list->remove_timeout_function) (timeout, + timeout_list->timeout_data); + + _dbus_timeout_unref (timeout); +} + +/** + * Sets a timeout to the given enabled state, invoking the + * application's DBusTimeoutToggledFunction if appropriate. + * + * @param timeout_list the timeout list. + * @param timeout the timeout to toggle. + * @param enabled #TRUE to enable + */ +void +_dbus_timeout_list_toggle_timeout (DBusTimeoutList *timeout_list, + DBusTimeout *timeout, + dbus_bool_t enabled) +{ + enabled = !!enabled; + + if (enabled == timeout->enabled) + return; + + timeout->enabled = enabled; + + if (timeout_list->timeout_toggled_function != NULL) + (* timeout_list->timeout_toggled_function) (timeout, + timeout_list->timeout_data); +} + +/** @} */ + +/** + * @defgroup DBusTimeout DBusTimeout + * @ingroup DBus + * @brief Object representing a timeout + * + * Types and functions related to DBusTimeout. A timeout + * represents a timeout that the main loop needs to monitor, + * as in Qt's QTimer or GLib's g_timeout_add(). + * + * Use dbus_connection_set_timeout_functions() or dbus_server_set_timeout_functions() + * to be notified when libdbus needs to add or remove timeouts. + * + * @{ + */ + + +/** + * @typedef DBusTimeout + * + * Opaque object representing a timeout. + */ + +/** + * Gets the timeout interval. The dbus_timeout_handle() + * should be called each time this interval elapses, + * starting after it elapses once. + * + * The interval may change during the life of the + * timeout; if so, the timeout will be disabled and + * re-enabled (calling the "timeout toggled function") + * to notify you of the change. + * + * @param timeout the DBusTimeout object. + * @returns the interval in milliseconds. + */ +int +dbus_timeout_get_interval (DBusTimeout *timeout) +{ + return timeout->interval; +} + +/** + * Gets data previously set with dbus_timeout_set_data() + * or #NULL if none. + * + * @param timeout the DBusTimeout object. + * @returns previously-set data. + */ +void* +dbus_timeout_get_data (DBusTimeout *timeout) +{ + return timeout->data; +} + +/** + * Sets data which can be retrieved with dbus_timeout_get_data(). + * Intended for use by the DBusAddTimeoutFunction and + * DBusRemoveTimeoutFunction to store their own data. For example with + * Qt you might store the QTimer for this timeout and with GLib + * you might store a g_timeout_add result id. + * + * @param timeout the DBusTimeout object. + * @param data the data. + * @param free_data_function function to be called to free the data. + */ +void +dbus_timeout_set_data (DBusTimeout *timeout, + void *data, + DBusFreeFunction free_data_function) +{ + if (timeout->free_data_function != NULL) + (* timeout->free_data_function) (timeout->data); + + timeout->data = data; + timeout->free_data_function = free_data_function; +} + +/** + * Calls the timeout handler for this timeout. + * This function should be called when the timeout + * occurs. + * + * If this function returns #FALSE, then there wasn't + * enough memory to handle the timeout. Typically just + * letting the timeout fire again next time it naturally + * times out is an adequate response to that problem, + * but you could try to do more if you wanted. + * + * @param timeout the DBusTimeout object. + * @returns #FALSE if there wasn't enough memory + */ +dbus_bool_t +dbus_timeout_handle (DBusTimeout *timeout) +{ + return (* timeout->handler) (timeout->handler_data); +} + + +/** + * Returns whether a timeout is enabled or not. If not + * enabled, it should not be polled by the main loop. + * + * @param timeout the DBusTimeout object + * @returns #TRUE if the timeout is enabled + */ +dbus_bool_t +dbus_timeout_get_enabled (DBusTimeout *timeout) +{ + return timeout->enabled; +} + +/** @} end public API docs */ diff --git a/src/dbus/dbus-timeout.h b/src/dbus/dbus-timeout.h new file mode 100644 index 0000000..b2312ee --- /dev/null +++ b/src/dbus/dbus-timeout.h @@ -0,0 +1,75 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-timeout.h DBusTimeout internal interfaces + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef DBUS_TIMEOUT_H +#define DBUS_TIMEOUT_H + +#include +#include + +DBUS_BEGIN_DECLS + +/** + * @addtogroup DBusTimeoutInternals + * @{ + */ + +/* Public methods on DBusTimeout are in dbus-connection.h */ + +typedef struct DBusTimeoutList DBusTimeoutList; + +/** function to run when the timeout is handled */ +typedef dbus_bool_t (* DBusTimeoutHandler) (void *data); + +DBusTimeout* _dbus_timeout_new (int interval, + DBusTimeoutHandler handler, + void *data, + DBusFreeFunction free_data_function); +DBusTimeout* _dbus_timeout_ref (DBusTimeout *timeout); +void _dbus_timeout_unref (DBusTimeout *timeout); +void _dbus_timeout_set_interval (DBusTimeout *timeout, + int interval); +void _dbus_timeout_set_enabled (DBusTimeout *timeout, + dbus_bool_t enabled); + +DBusTimeoutList *_dbus_timeout_list_new (void); +void _dbus_timeout_list_free (DBusTimeoutList *timeout_list); +dbus_bool_t _dbus_timeout_list_set_functions (DBusTimeoutList *timeout_list, + DBusAddTimeoutFunction add_function, + DBusRemoveTimeoutFunction remove_function, + DBusTimeoutToggledFunction toggled_function, + void *data, + DBusFreeFunction free_data_function); +dbus_bool_t _dbus_timeout_list_add_timeout (DBusTimeoutList *timeout_list, + DBusTimeout *timeout); +void _dbus_timeout_list_remove_timeout (DBusTimeoutList *timeout_list, + DBusTimeout *timeout); +void _dbus_timeout_list_toggle_timeout (DBusTimeoutList *timeout_list, + DBusTimeout *timeout, + dbus_bool_t enabled); + + +/** @} */ + +DBUS_END_DECLS + +#endif /* DBUS_TIMEOUT_H */ diff --git a/src/dbus/dbus-transport-protected.h b/src/dbus/dbus-transport-protected.h new file mode 100644 index 0000000..4d56a72 --- /dev/null +++ b/src/dbus/dbus-transport-protected.h @@ -0,0 +1,143 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-transport-protected.h Used by subclasses of DBusTransport object (internal to D-Bus implementation) + * + * Copyright (C) 2002, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef DBUS_TRANSPORT_PROTECTED_H +#define DBUS_TRANSPORT_PROTECTED_H + +#include +#include +#include +#include +#include +#include + +DBUS_BEGIN_DECLS + +typedef struct DBusTransportVTable DBusTransportVTable; + +/** + * The virtual table that must be implemented to + * create a new kind of transport. + */ +struct DBusTransportVTable +{ + void (* finalize) (DBusTransport *transport); + /**< The finalize method must free the transport. */ + + dbus_bool_t (* handle_watch) (DBusTransport *transport, + DBusWatch *watch, + unsigned int flags); + /**< The handle_watch method handles reading/writing + * data as indicated by the flags. + */ + + void (* disconnect) (DBusTransport *transport); + /**< Disconnect this transport. */ + + dbus_bool_t (* connection_set) (DBusTransport *transport); + /**< Called when transport->connection has been filled in */ + + void (* do_iteration) (DBusTransport *transport, + unsigned int flags, + int timeout_milliseconds); + /**< Called to do a single "iteration" (block on select/poll + * followed by reading or writing data). + */ + + void (* live_messages_changed) (DBusTransport *transport); + /**< Outstanding messages counter changed */ + + dbus_bool_t (* get_socket_fd) (DBusTransport *transport, + int *fd_p); + /**< Get socket file descriptor */ +}; + +/** + * Object representing a transport such as a socket. + * A transport can shuttle messages from point A to point B, + * and is the backend for a #DBusConnection. + * + */ +struct DBusTransport +{ + int refcount; /**< Reference count. */ + + const DBusTransportVTable *vtable; /**< Virtual methods for this instance. */ + + DBusConnection *connection; /**< Connection owning this transport. */ + + DBusMessageLoader *loader; /**< Message-loading buffer. */ + + DBusAuth *auth; /**< Authentication conversation */ + + DBusCredentials *credentials; /**< Credentials of other end read from the socket */ + + long max_live_messages_size; /**< Max total size of received messages. */ + + DBusCounter *live_messages_size; /**< Counter for size of all live messages. */ + + + char *address; /**< Address of the server we are connecting to (#NULL for the server side of a transport) */ + + char *expected_guid; /**< GUID we expect the server to have, #NULL on server side or if we don't have an expectation */ + + DBusAllowUnixUserFunction unix_user_function; /**< Function for checking whether a user is authorized. */ + void *unix_user_data; /**< Data for unix_user_function */ + + DBusFreeFunction free_unix_user_data; /**< Function to free unix_user_data */ + + DBusAllowWindowsUserFunction windows_user_function; /**< Function for checking whether a user is authorized. */ + void *windows_user_data; /**< Data for windows_user_function */ + + DBusFreeFunction free_windows_user_data; /**< Function to free windows_user_data */ + + unsigned int disconnected : 1; /**< #TRUE if we are disconnected. */ + unsigned int authenticated : 1; /**< Cache of auth state; use _dbus_transport_get_is_authenticated() to query value */ + unsigned int send_credentials_pending : 1; /**< #TRUE if we need to send credentials */ + unsigned int receive_credentials_pending : 1; /**< #TRUE if we need to receive credentials */ + unsigned int is_server : 1; /**< #TRUE if on the server side */ + unsigned int unused_bytes_recovered : 1; /**< #TRUE if we've recovered unused bytes from auth */ + unsigned int allow_anonymous : 1; /**< #TRUE if an anonymous client can connect */ +}; + +dbus_bool_t _dbus_transport_init_base (DBusTransport *transport, + const DBusTransportVTable *vtable, + const DBusString *server_guid, + const DBusString *address); +void _dbus_transport_finalize_base (DBusTransport *transport); + + +typedef enum +{ + DBUS_TRANSPORT_OPEN_NOT_HANDLED, /**< we aren't in charge of this address type */ + DBUS_TRANSPORT_OPEN_OK, /**< we set up the listen */ + DBUS_TRANSPORT_OPEN_BAD_ADDRESS, /**< malformed address */ + DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT /**< well-formed address but failed to set it up */ +} DBusTransportOpenResult; + +DBusTransportOpenResult _dbus_transport_open_platform_specific (DBusAddressEntry *entry, + DBusTransport **transport_p, + DBusError *error); + +DBUS_END_DECLS + +#endif /* DBUS_TRANSPORT_PROTECTED_H */ diff --git a/src/dbus/dbus-transport-socket.c b/src/dbus/dbus-transport-socket.c new file mode 100644 index 0000000..6d7c89c --- /dev/null +++ b/src/dbus/dbus-transport-socket.c @@ -0,0 +1,1339 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-transport-socket.c Socket subclasses of DBusTransport + * + * Copyright (C) 2002, 2003, 2004, 2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-internals.h" +#include "dbus-connection-internal.h" +#include "dbus-transport-socket.h" +#include "dbus-transport-protected.h" +#include "dbus-watch.h" +#include "dbus-credentials.h" + + +/** + * @defgroup DBusTransportSocket DBusTransport implementations for sockets + * @ingroup DBusInternals + * @brief Implementation details of DBusTransport on sockets + * + * @{ + */ + +/** + * Opaque object representing a socket file descriptor transport. + */ +typedef struct DBusTransportSocket DBusTransportSocket; + +/** + * Implementation details of DBusTransportSocket. All members are private. + */ +struct DBusTransportSocket +{ + DBusTransport base; /**< Parent instance */ + int fd; /**< File descriptor. */ + DBusWatch *read_watch; /**< Watch for readability. */ + DBusWatch *write_watch; /**< Watch for writability. */ + + int max_bytes_read_per_iteration; /**< To avoid blocking too long. */ + int max_bytes_written_per_iteration; /**< To avoid blocking too long. */ + + int message_bytes_written; /**< Number of bytes of current + * outgoing message that have + * been written. + */ + DBusString encoded_outgoing; /**< Encoded version of current + * outgoing message. + */ + DBusString encoded_incoming; /**< Encoded version of current + * incoming data. + */ +}; + +static void +free_watches (DBusTransport *transport) +{ + DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport; + + _dbus_verbose ("%s start\n", _DBUS_FUNCTION_NAME); + + if (socket_transport->read_watch) + { + if (transport->connection) + _dbus_connection_remove_watch_unlocked (transport->connection, + socket_transport->read_watch); + _dbus_watch_invalidate (socket_transport->read_watch); + _dbus_watch_unref (socket_transport->read_watch); + socket_transport->read_watch = NULL; + } + + if (socket_transport->write_watch) + { + if (transport->connection) + _dbus_connection_remove_watch_unlocked (transport->connection, + socket_transport->write_watch); + _dbus_watch_invalidate (socket_transport->write_watch); + _dbus_watch_unref (socket_transport->write_watch); + socket_transport->write_watch = NULL; + } + + _dbus_verbose ("%s end\n", _DBUS_FUNCTION_NAME); +} + +static void +socket_finalize (DBusTransport *transport) +{ + DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport; + + _dbus_verbose ("%s\n", _DBUS_FUNCTION_NAME); + + free_watches (transport); + + _dbus_string_free (&socket_transport->encoded_outgoing); + _dbus_string_free (&socket_transport->encoded_incoming); + + _dbus_transport_finalize_base (transport); + + _dbus_assert (socket_transport->read_watch == NULL); + _dbus_assert (socket_transport->write_watch == NULL); + + dbus_free (transport); +} + +static void +check_write_watch (DBusTransport *transport) +{ + DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport; + dbus_bool_t needed; + + if (transport->connection == NULL) + return; + + if (transport->disconnected) + { + _dbus_assert (socket_transport->write_watch == NULL); + return; + } + + _dbus_transport_ref (transport); + + if (_dbus_transport_get_is_authenticated (transport)) + needed = _dbus_connection_has_messages_to_send_unlocked (transport->connection); + else + { + if (transport->send_credentials_pending) + needed = TRUE; + else + { + DBusAuthState auth_state; + + auth_state = _dbus_auth_do_work (transport->auth); + + /* If we need memory we install the write watch just in case, + * if there's no need for it, it will get de-installed + * next time we try reading. + */ + if (auth_state == DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND || + auth_state == DBUS_AUTH_STATE_WAITING_FOR_MEMORY) + needed = TRUE; + else + needed = FALSE; + } + } + + _dbus_verbose ("check_write_watch(): needed = %d on connection %p watch %p fd = %d outgoing messages exist %d\n", + needed, transport->connection, socket_transport->write_watch, + socket_transport->fd, + _dbus_connection_has_messages_to_send_unlocked (transport->connection)); + + _dbus_connection_toggle_watch_unlocked (transport->connection, + socket_transport->write_watch, + needed); + + _dbus_transport_unref (transport); +} + +static void +check_read_watch (DBusTransport *transport) +{ + DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport; + dbus_bool_t need_read_watch; + + _dbus_verbose ("%s: fd = %d\n", + _DBUS_FUNCTION_NAME, socket_transport->fd); + + if (transport->connection == NULL) + return; + + if (transport->disconnected) + { + _dbus_assert (socket_transport->read_watch == NULL); + return; + } + + _dbus_transport_ref (transport); + + if (_dbus_transport_get_is_authenticated (transport)) + need_read_watch = + _dbus_counter_get_value (transport->live_messages_size) < transport->max_live_messages_size; + else + { + if (transport->receive_credentials_pending) + need_read_watch = TRUE; + else + { + /* The reason to disable need_read_watch when not WAITING_FOR_INPUT + * is to avoid spinning on the file descriptor when we're waiting + * to write or for some other part of the auth process + */ + DBusAuthState auth_state; + + auth_state = _dbus_auth_do_work (transport->auth); + + /* If we need memory we install the read watch just in case, + * if there's no need for it, it will get de-installed + * next time we try reading. If we're authenticated we + * install it since we normally have it installed while + * authenticated. + */ + if (auth_state == DBUS_AUTH_STATE_WAITING_FOR_INPUT || + auth_state == DBUS_AUTH_STATE_WAITING_FOR_MEMORY || + auth_state == DBUS_AUTH_STATE_AUTHENTICATED) + need_read_watch = TRUE; + else + need_read_watch = FALSE; + } + } + + _dbus_verbose (" setting read watch enabled = %d\n", need_read_watch); + _dbus_connection_toggle_watch_unlocked (transport->connection, + socket_transport->read_watch, + need_read_watch); + + _dbus_transport_unref (transport); +} + +static void +do_io_error (DBusTransport *transport) +{ + _dbus_transport_ref (transport); + _dbus_transport_disconnect (transport); + _dbus_transport_unref (transport); +} + +/* return value is whether we successfully read any new data. */ +static dbus_bool_t +read_data_into_auth (DBusTransport *transport, + dbus_bool_t *oom) +{ + DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport; + DBusString *buffer; + int bytes_read; + + *oom = FALSE; + + _dbus_auth_get_buffer (transport->auth, &buffer); + + bytes_read = _dbus_read_socket (socket_transport->fd, + buffer, socket_transport->max_bytes_read_per_iteration); + + _dbus_auth_return_buffer (transport->auth, buffer, + bytes_read > 0 ? bytes_read : 0); + + if (bytes_read > 0) + { + _dbus_verbose (" read %d bytes in auth phase\n", bytes_read); + + return TRUE; + } + else if (bytes_read < 0) + { + /* EINTR already handled for us */ + + if (_dbus_get_is_errno_enomem ()) + { + *oom = TRUE; + } + else if (_dbus_get_is_errno_eagain_or_ewouldblock ()) + ; /* do nothing, just return FALSE below */ + else + { + _dbus_verbose ("Error reading from remote app: %s\n", + _dbus_strerror_from_errno ()); + do_io_error (transport); + } + + return FALSE; + } + else + { + _dbus_assert (bytes_read == 0); + + _dbus_verbose ("Disconnected from remote app\n"); + do_io_error (transport); + + return FALSE; + } +} + +/* Return value is whether we successfully wrote any bytes */ +static dbus_bool_t +write_data_from_auth (DBusTransport *transport) +{ + DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport; + int bytes_written; + const DBusString *buffer; + + if (!_dbus_auth_get_bytes_to_send (transport->auth, + &buffer)) + return FALSE; + + bytes_written = _dbus_write_socket (socket_transport->fd, + buffer, + 0, _dbus_string_get_length (buffer)); + + if (bytes_written > 0) + { + _dbus_auth_bytes_sent (transport->auth, bytes_written); + return TRUE; + } + else if (bytes_written < 0) + { + /* EINTR already handled for us */ + + if (_dbus_get_is_errno_eagain_or_ewouldblock ()) + ; + else + { + _dbus_verbose ("Error writing to remote app: %s\n", + _dbus_strerror_from_errno ()); + do_io_error (transport); + } + } + + return FALSE; +} + +/* FALSE on OOM */ +static dbus_bool_t +exchange_credentials (DBusTransport *transport, + dbus_bool_t do_reading, + dbus_bool_t do_writing) +{ + DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport; + DBusError error = DBUS_ERROR_INIT; + + _dbus_verbose ("exchange_credentials: do_reading = %d, do_writing = %d\n", + do_reading, do_writing); + + if (do_writing && transport->send_credentials_pending) + { + if (_dbus_send_credentials_socket (socket_transport->fd, + &error)) + { + transport->send_credentials_pending = FALSE; + } + else + { + _dbus_verbose ("Failed to write credentials: %s\n", error.message); + dbus_error_free (&error); + do_io_error (transport); + } + } + + if (do_reading && transport->receive_credentials_pending) + { + /* FIXME this can fail due to IO error _or_ OOM, broken + * (somewhat tricky to fix since the OOM error can be set after + * we already read the credentials byte, so basically we need to + * separate reading the byte and storing it in the + * transport->credentials). Does not really matter for now + * because storing in credentials never actually fails on unix. + */ + if (_dbus_read_credentials_socket (socket_transport->fd, + transport->credentials, + &error)) + { + transport->receive_credentials_pending = FALSE; + } + else + { + _dbus_verbose ("Failed to read credentials %s\n", error.message); + dbus_error_free (&error); + do_io_error (transport); + } + } + + if (!(transport->send_credentials_pending || + transport->receive_credentials_pending)) + { + if (!_dbus_auth_set_credentials (transport->auth, + transport->credentials)) + return FALSE; + } + + return TRUE; +} + +static dbus_bool_t +do_authentication (DBusTransport *transport, + dbus_bool_t do_reading, + dbus_bool_t do_writing, + dbus_bool_t *auth_completed) +{ + dbus_bool_t oom; + dbus_bool_t orig_auth_state; + + oom = FALSE; + + orig_auth_state = _dbus_transport_get_is_authenticated (transport); + + /* This is essential to avoid the check_write_watch() at the end, + * we don't want to add a write watch in do_iteration before + * we try writing and get EAGAIN + */ + if (orig_auth_state) + { + if (auth_completed) + *auth_completed = FALSE; + return TRUE; + } + + _dbus_transport_ref (transport); + + while (!_dbus_transport_get_is_authenticated (transport) && + _dbus_transport_get_is_connected (transport)) + { + if (!exchange_credentials (transport, do_reading, do_writing)) + { + /* OOM */ + oom = TRUE; + goto out; + } + + if (transport->send_credentials_pending || + transport->receive_credentials_pending) + { + _dbus_verbose ("send_credentials_pending = %d receive_credentials_pending = %d\n", + transport->send_credentials_pending, + transport->receive_credentials_pending); + goto out; + } + +#define TRANSPORT_SIDE(t) ((t)->is_server ? "server" : "client") + switch (_dbus_auth_do_work (transport->auth)) + { + case DBUS_AUTH_STATE_WAITING_FOR_INPUT: + _dbus_verbose (" %s auth state: waiting for input\n", + TRANSPORT_SIDE (transport)); + if (!do_reading || !read_data_into_auth (transport, &oom)) + goto out; + break; + + case DBUS_AUTH_STATE_WAITING_FOR_MEMORY: + _dbus_verbose (" %s auth state: waiting for memory\n", + TRANSPORT_SIDE (transport)); + oom = TRUE; + goto out; + break; + + case DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND: + _dbus_verbose (" %s auth state: bytes to send\n", + TRANSPORT_SIDE (transport)); + if (!do_writing || !write_data_from_auth (transport)) + goto out; + break; + + case DBUS_AUTH_STATE_NEED_DISCONNECT: + _dbus_verbose (" %s auth state: need to disconnect\n", + TRANSPORT_SIDE (transport)); + do_io_error (transport); + break; + + case DBUS_AUTH_STATE_AUTHENTICATED: + _dbus_verbose (" %s auth state: authenticated\n", + TRANSPORT_SIDE (transport)); + break; + } + } + + out: + if (auth_completed) + *auth_completed = (orig_auth_state != _dbus_transport_get_is_authenticated (transport)); + + check_read_watch (transport); + check_write_watch (transport); + _dbus_transport_unref (transport); + + if (oom) + return FALSE; + else + return TRUE; +} + +/* returns false on oom */ +static dbus_bool_t +do_writing (DBusTransport *transport) +{ + int total; + DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport; + dbus_bool_t oom; + + /* No messages without authentication! */ + if (!_dbus_transport_get_is_authenticated (transport)) + { + _dbus_verbose ("Not authenticated, not writing anything\n"); + return TRUE; + } + + if (transport->disconnected) + { + _dbus_verbose ("Not connected, not writing anything\n"); + return TRUE; + } + +#if 1 + _dbus_verbose ("do_writing(), have_messages = %d, fd = %d\n", + _dbus_connection_has_messages_to_send_unlocked (transport->connection), + socket_transport->fd); +#endif + + oom = FALSE; + total = 0; + + while (!transport->disconnected && + _dbus_connection_has_messages_to_send_unlocked (transport->connection)) + { + int bytes_written; + DBusMessage *message; + const DBusString *header; + const DBusString *body; + int header_len, body_len; + int total_bytes_to_write; + + if (total > socket_transport->max_bytes_written_per_iteration) + { + _dbus_verbose ("%d bytes exceeds %d bytes written per iteration, returning\n", + total, socket_transport->max_bytes_written_per_iteration); + goto out; + } + + message = _dbus_connection_get_message_to_send (transport->connection); + _dbus_assert (message != NULL); + dbus_message_lock (message); + +#if 0 + _dbus_verbose ("writing message %p\n", message); +#endif + + _dbus_message_get_network_data (message, + &header, &body); + + header_len = _dbus_string_get_length (header); + body_len = _dbus_string_get_length (body); + + if (_dbus_auth_needs_encoding (transport->auth)) + { + if (_dbus_string_get_length (&socket_transport->encoded_outgoing) == 0) + { + if (!_dbus_auth_encode_data (transport->auth, + header, &socket_transport->encoded_outgoing)) + { + oom = TRUE; + goto out; + } + + if (!_dbus_auth_encode_data (transport->auth, + body, &socket_transport->encoded_outgoing)) + { + _dbus_string_set_length (&socket_transport->encoded_outgoing, 0); + oom = TRUE; + goto out; + } + } + + total_bytes_to_write = _dbus_string_get_length (&socket_transport->encoded_outgoing); + +#if 0 + _dbus_verbose ("encoded message is %d bytes\n", + total_bytes_to_write); +#endif + + bytes_written = + _dbus_write_socket (socket_transport->fd, + &socket_transport->encoded_outgoing, + socket_transport->message_bytes_written, + total_bytes_to_write - socket_transport->message_bytes_written); + } + else + { + total_bytes_to_write = header_len + body_len; + +#if 0 + _dbus_verbose ("message is %d bytes\n", + total_bytes_to_write); +#endif + + if (socket_transport->message_bytes_written < header_len) + { + bytes_written = + _dbus_write_socket_two (socket_transport->fd, + header, + socket_transport->message_bytes_written, + header_len - socket_transport->message_bytes_written, + body, + 0, body_len); + } + else + { + bytes_written = + _dbus_write_socket (socket_transport->fd, + body, + (socket_transport->message_bytes_written - header_len), + body_len - + (socket_transport->message_bytes_written - header_len)); + } + } + + if (bytes_written < 0) + { + /* EINTR already handled for us */ + + if (_dbus_get_is_errno_eagain_or_ewouldblock ()) + goto out; + else + { + _dbus_verbose ("Error writing to remote app: %s\n", + _dbus_strerror_from_errno ()); + do_io_error (transport); + goto out; + } + } + else + { + _dbus_verbose (" wrote %d bytes of %d\n", bytes_written, + total_bytes_to_write); + + total += bytes_written; + socket_transport->message_bytes_written += bytes_written; + + _dbus_assert (socket_transport->message_bytes_written <= + total_bytes_to_write); + + if (socket_transport->message_bytes_written == total_bytes_to_write) + { + socket_transport->message_bytes_written = 0; + _dbus_string_set_length (&socket_transport->encoded_outgoing, 0); + _dbus_string_compact (&socket_transport->encoded_outgoing, 2048); + + _dbus_connection_message_sent (transport->connection, + message); + } + } + } + + out: + if (oom) + return FALSE; + else + return TRUE; +} + +/* returns false on out-of-memory */ +static dbus_bool_t +do_reading (DBusTransport *transport) +{ + DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport; + DBusString *buffer; + int bytes_read; + int total; + dbus_bool_t oom; + + _dbus_verbose ("%s: fd = %d\n", _DBUS_FUNCTION_NAME, + socket_transport->fd); + + /* No messages without authentication! */ + if (!_dbus_transport_get_is_authenticated (transport)) + return TRUE; + + oom = FALSE; + + total = 0; + + again: + + /* See if we've exceeded max messages and need to disable reading */ + check_read_watch (transport); + + if (total > socket_transport->max_bytes_read_per_iteration) + { + _dbus_verbose ("%d bytes exceeds %d bytes read per iteration, returning\n", + total, socket_transport->max_bytes_read_per_iteration); + goto out; + } + + _dbus_assert (socket_transport->read_watch != NULL || + transport->disconnected); + + if (transport->disconnected) + goto out; + + if (!dbus_watch_get_enabled (socket_transport->read_watch)) + return TRUE; + + if (_dbus_auth_needs_decoding (transport->auth)) + { + if (_dbus_string_get_length (&socket_transport->encoded_incoming) > 0) + bytes_read = _dbus_string_get_length (&socket_transport->encoded_incoming); + else + bytes_read = _dbus_read_socket (socket_transport->fd, + &socket_transport->encoded_incoming, + socket_transport->max_bytes_read_per_iteration); + + _dbus_assert (_dbus_string_get_length (&socket_transport->encoded_incoming) == + bytes_read); + + if (bytes_read > 0) + { + int orig_len; + + _dbus_message_loader_get_buffer (transport->loader, + &buffer); + + orig_len = _dbus_string_get_length (buffer); + + if (!_dbus_auth_decode_data (transport->auth, + &socket_transport->encoded_incoming, + buffer)) + { + _dbus_verbose ("Out of memory decoding incoming data\n"); + _dbus_message_loader_return_buffer (transport->loader, + buffer, + _dbus_string_get_length (buffer) - orig_len); + + oom = TRUE; + goto out; + } + + _dbus_message_loader_return_buffer (transport->loader, + buffer, + _dbus_string_get_length (buffer) - orig_len); + + _dbus_string_set_length (&socket_transport->encoded_incoming, 0); + _dbus_string_compact (&socket_transport->encoded_incoming, 2048); + } + } + else + { + _dbus_message_loader_get_buffer (transport->loader, + &buffer); + + bytes_read = _dbus_read_socket (socket_transport->fd, + buffer, socket_transport->max_bytes_read_per_iteration); + + _dbus_message_loader_return_buffer (transport->loader, + buffer, + bytes_read < 0 ? 0 : bytes_read); + } + + if (bytes_read < 0) + { + /* EINTR already handled for us */ + + if (_dbus_get_is_errno_enomem ()) + { + _dbus_verbose ("Out of memory in read()/do_reading()\n"); + oom = TRUE; + goto out; + } + else if (_dbus_get_is_errno_eagain_or_ewouldblock ()) + goto out; + else + { + _dbus_verbose ("Error reading from remote app: %s\n", + _dbus_strerror_from_errno ()); + do_io_error (transport); + goto out; + } + } + else if (bytes_read == 0) + { + _dbus_verbose ("Disconnected from remote app\n"); + do_io_error (transport); + goto out; + } + else + { + _dbus_verbose (" read %d bytes\n", bytes_read); + + total += bytes_read; + + if (!_dbus_transport_queue_messages (transport)) + { + oom = TRUE; + _dbus_verbose (" out of memory when queueing messages we just read in the transport\n"); + goto out; + } + + /* Try reading more data until we get EAGAIN and return, or + * exceed max bytes per iteration. If in blocking mode of + * course we'll block instead of returning. + */ + goto again; + } + + out: + if (oom) + return FALSE; + else + return TRUE; +} + +static dbus_bool_t +socket_handle_watch (DBusTransport *transport, + DBusWatch *watch, + unsigned int flags) +{ + DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport; + + _dbus_assert (watch == socket_transport->read_watch || + watch == socket_transport->write_watch); + _dbus_assert (watch != NULL); + + /* Disconnect in case of an error. In case of hangup do not + * disconnect the transport because data can still be in the buffer + * and do_reading may need several iteration to read it all (because + * of its max_bytes_read_per_iteration limit). The condition where + * flags == HANGUP (without READABLE) probably never happen in fact. + */ + if ((flags & DBUS_WATCH_ERROR) || + ((flags & DBUS_WATCH_HANGUP) && !(flags & DBUS_WATCH_READABLE))) + { + _dbus_verbose ("Hang up or error on watch\n"); + _dbus_transport_disconnect (transport); + return TRUE; + } + + if (watch == socket_transport->read_watch && + (flags & DBUS_WATCH_READABLE)) + { + dbus_bool_t auth_finished; +#if 1 + _dbus_verbose ("handling read watch %p flags = %x\n", + watch, flags); +#endif + if (!do_authentication (transport, TRUE, FALSE, &auth_finished)) + return FALSE; + + /* We don't want to do a read immediately following + * a successful authentication. This is so we + * have a chance to propagate the authentication + * state further up. Specifically, we need to + * process any pending data from the auth object. + */ + if (!auth_finished) + { + if (!do_reading (transport)) + { + _dbus_verbose ("no memory to read\n"); + return FALSE; + } + } + else + { + _dbus_verbose ("Not reading anything since we just completed the authentication\n"); + } + } + else if (watch == socket_transport->write_watch && + (flags & DBUS_WATCH_WRITABLE)) + { +#if 1 + _dbus_verbose ("handling write watch, have_outgoing_messages = %d\n", + _dbus_connection_has_messages_to_send_unlocked (transport->connection)); +#endif + if (!do_authentication (transport, FALSE, TRUE, NULL)) + return FALSE; + + if (!do_writing (transport)) + { + _dbus_verbose ("no memory to write\n"); + return FALSE; + } + + /* See if we still need the write watch */ + check_write_watch (transport); + } +#ifdef DBUS_ENABLE_VERBOSE_MODE + else + { + if (watch == socket_transport->read_watch) + _dbus_verbose ("asked to handle read watch with non-read condition 0x%x\n", + flags); + else if (watch == socket_transport->write_watch) + _dbus_verbose ("asked to handle write watch with non-write condition 0x%x\n", + flags); + else + _dbus_verbose ("asked to handle watch %p on fd %d that we don't recognize\n", + watch, dbus_watch_get_socket (watch)); + } +#endif /* DBUS_ENABLE_VERBOSE_MODE */ + + return TRUE; +} + +static void +socket_disconnect (DBusTransport *transport) +{ + DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport; + + _dbus_verbose ("%s\n", _DBUS_FUNCTION_NAME); + + free_watches (transport); + + _dbus_close_socket (socket_transport->fd, NULL); + socket_transport->fd = -1; +} + +static dbus_bool_t +socket_connection_set (DBusTransport *transport) +{ + DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport; + + _dbus_watch_set_handler (socket_transport->write_watch, + _dbus_connection_handle_watch, + transport->connection, NULL); + + _dbus_watch_set_handler (socket_transport->read_watch, + _dbus_connection_handle_watch, + transport->connection, NULL); + + if (!_dbus_connection_add_watch_unlocked (transport->connection, + socket_transport->write_watch)) + return FALSE; + + if (!_dbus_connection_add_watch_unlocked (transport->connection, + socket_transport->read_watch)) + { + _dbus_connection_remove_watch_unlocked (transport->connection, + socket_transport->write_watch); + return FALSE; + } + + check_read_watch (transport); + check_write_watch (transport); + + return TRUE; +} + +/** + * @todo We need to have a way to wake up the select sleep if + * a new iteration request comes in with a flag (read/write) that + * we're not currently serving. Otherwise a call that just reads + * could block a write call forever (if there are no incoming + * messages). + */ +static void +socket_do_iteration (DBusTransport *transport, + unsigned int flags, + int timeout_milliseconds) +{ + DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport; + DBusPollFD poll_fd; + int poll_res; + int poll_timeout; + + _dbus_verbose (" iteration flags = %s%s timeout = %d read_watch = %p write_watch = %p fd = %d\n", + flags & DBUS_ITERATION_DO_READING ? "read" : "", + flags & DBUS_ITERATION_DO_WRITING ? "write" : "", + timeout_milliseconds, + socket_transport->read_watch, + socket_transport->write_watch, + socket_transport->fd); + + /* the passed in DO_READING/DO_WRITING flags indicate whether to + * read/write messages, but regardless of those we may need to block + * for reading/writing to do auth. But if we do reading for auth, + * we don't want to read any messages yet if not given DO_READING. + */ + + poll_fd.fd = socket_transport->fd; + poll_fd.events = 0; + + if (_dbus_transport_get_is_authenticated (transport)) + { + /* This is kind of a hack; if we have stuff to write, then try + * to avoid the poll. This is probably about a 5% speedup on an + * echo client/server. + * + * If both reading and writing were requested, we want to avoid this + * since it could have funky effects: + * - both ends spinning waiting for the other one to read + * data so they can finish writing + * - prioritizing all writing ahead of reading + */ + if ((flags & DBUS_ITERATION_DO_WRITING) && + !(flags & (DBUS_ITERATION_DO_READING | DBUS_ITERATION_BLOCK)) && + !transport->disconnected && + _dbus_connection_has_messages_to_send_unlocked (transport->connection)) + { + do_writing (transport); + + if (transport->disconnected || + !_dbus_connection_has_messages_to_send_unlocked (transport->connection)) + goto out; + } + + /* If we get here, we decided to do the poll() after all */ + _dbus_assert (socket_transport->read_watch); + if (flags & DBUS_ITERATION_DO_READING) + poll_fd.events |= _DBUS_POLLIN; + + _dbus_assert (socket_transport->write_watch); + if (flags & DBUS_ITERATION_DO_WRITING) + poll_fd.events |= _DBUS_POLLOUT; + } + else + { + DBusAuthState auth_state; + + auth_state = _dbus_auth_do_work (transport->auth); + + if (transport->receive_credentials_pending || + auth_state == DBUS_AUTH_STATE_WAITING_FOR_INPUT) + poll_fd.events |= _DBUS_POLLIN; + + if (transport->send_credentials_pending || + auth_state == DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND) + poll_fd.events |= _DBUS_POLLOUT; + } + + if (poll_fd.events) + { + if (flags & DBUS_ITERATION_BLOCK) + poll_timeout = timeout_milliseconds; + else + poll_timeout = 0; + + /* For blocking selects we drop the connection lock here + * to avoid blocking out connection access during a potentially + * indefinite blocking call. The io path is still protected + * by the io_path_cond condvar, so we won't reenter this. + */ + if (flags & DBUS_ITERATION_BLOCK) + { + _dbus_verbose ("unlock %s pre poll\n", _DBUS_FUNCTION_NAME); + _dbus_connection_unlock (transport->connection); + } + + again: + poll_res = _dbus_poll (&poll_fd, 1, poll_timeout); + + if (poll_res < 0 && _dbus_get_is_errno_eintr ()) + goto again; + + if (flags & DBUS_ITERATION_BLOCK) + { + _dbus_verbose ("lock %s post poll\n", _DBUS_FUNCTION_NAME); + _dbus_connection_lock (transport->connection); + } + + if (poll_res >= 0) + { + if (poll_res == 0) + poll_fd.revents = 0; /* some concern that posix does not guarantee this; + * valgrind flags it as an error. though it probably + * is guaranteed on linux at least. + */ + + if (poll_fd.revents & _DBUS_POLLERR) + do_io_error (transport); + else + { + dbus_bool_t need_read = (poll_fd.revents & _DBUS_POLLIN) > 0; + dbus_bool_t need_write = (poll_fd.revents & _DBUS_POLLOUT) > 0; + dbus_bool_t authentication_completed; + + _dbus_verbose ("in iteration, need_read=%d need_write=%d\n", + need_read, need_write); + do_authentication (transport, need_read, need_write, + &authentication_completed); + + /* See comment in socket_handle_watch. */ + if (authentication_completed) + goto out; + + if (need_read && (flags & DBUS_ITERATION_DO_READING)) + do_reading (transport); + if (need_write && (flags & DBUS_ITERATION_DO_WRITING)) + do_writing (transport); + } + } + else + { + _dbus_verbose ("Error from _dbus_poll(): %s\n", + _dbus_strerror_from_errno ()); + } + } + + + out: + /* We need to install the write watch only if we did not + * successfully write everything. Note we need to be careful that we + * don't call check_write_watch *before* do_writing, since it's + * inefficient to add the write watch, and we can avoid it most of + * the time since we can write immediately. + * + * However, we MUST always call check_write_watch(); DBusConnection code + * relies on the fact that running an iteration will notice that + * messages are pending. + */ + check_write_watch (transport); + + _dbus_verbose (" ... leaving do_iteration()\n"); +} + +static void +socket_live_messages_changed (DBusTransport *transport) +{ + /* See if we should look for incoming messages again */ + check_read_watch (transport); +} + + +static dbus_bool_t +socket_get_socket_fd (DBusTransport *transport, + int *fd_p) +{ + DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport; + + *fd_p = socket_transport->fd; + + return TRUE; +} + +static const DBusTransportVTable socket_vtable = { + socket_finalize, + socket_handle_watch, + socket_disconnect, + socket_connection_set, + socket_do_iteration, + socket_live_messages_changed, + socket_get_socket_fd +}; + +/** + * Creates a new transport for the given socket file descriptor. The file + * descriptor must be nonblocking (use _dbus_set_fd_nonblocking() to + * make it so). This function is shared by various transports that + * boil down to a full duplex file descriptor. + * + * @param fd the file descriptor. + * @param server_guid non-#NULL if this transport is on the server side of a connection + * @param address the transport's address + * @returns the new transport, or #NULL if no memory. + */ +DBusTransport* +_dbus_transport_new_for_socket (int fd, + const DBusString *server_guid, + const DBusString *address) +{ + DBusTransportSocket *socket_transport; + + socket_transport = dbus_new0 (DBusTransportSocket, 1); + if (socket_transport == NULL) + return NULL; + + if (!_dbus_string_init (&socket_transport->encoded_outgoing)) + goto failed_0; + + if (!_dbus_string_init (&socket_transport->encoded_incoming)) + goto failed_1; + + socket_transport->write_watch = _dbus_watch_new (fd, + DBUS_WATCH_WRITABLE, + FALSE, + NULL, NULL, NULL); + if (socket_transport->write_watch == NULL) + goto failed_2; + + socket_transport->read_watch = _dbus_watch_new (fd, + DBUS_WATCH_READABLE, + FALSE, + NULL, NULL, NULL); + if (socket_transport->read_watch == NULL) + goto failed_3; + + if (!_dbus_transport_init_base (&socket_transport->base, + &socket_vtable, + server_guid, address)) + goto failed_4; + + socket_transport->fd = fd; + socket_transport->message_bytes_written = 0; + + /* These values should probably be tunable or something. */ + socket_transport->max_bytes_read_per_iteration = 2048; + socket_transport->max_bytes_written_per_iteration = 2048; + + return (DBusTransport*) socket_transport; + + failed_4: + _dbus_watch_unref (socket_transport->read_watch); + failed_3: + _dbus_watch_unref (socket_transport->write_watch); + failed_2: + _dbus_string_free (&socket_transport->encoded_incoming); + failed_1: + _dbus_string_free (&socket_transport->encoded_outgoing); + failed_0: + dbus_free (socket_transport); + return NULL; +} + +/** + * Creates a new transport for the given hostname and port. + * If host is NULL, it will default to localhost + * + * @param host the host to connect to + * @param port the port to connect to + * @param family the address family to connect to + * @param error location to store reason for failure. + * @returns a new transport, or #NULL on failure. + */ +DBusTransport* +_dbus_transport_new_for_tcp_socket (const char *host, + const char *port, + const char *family, + DBusError *error) +{ + int fd; + DBusTransport *transport; + DBusString address; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + if (!_dbus_string_init (&address)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return NULL; + } + + if (host == NULL) + host = "localhost"; + + if (!_dbus_string_append (&address, "tcp:")) + goto error; + + if (!_dbus_string_append (&address, "host=") || + !_dbus_string_append (&address, host)) + goto error; + + if (!_dbus_string_append (&address, ",port=") || + !_dbus_string_append (&address, port)) + goto error; + + if (family != NULL && + (!_dbus_string_append (&address, "family=") || + !_dbus_string_append (&address, family))) + goto error; + + fd = _dbus_connect_tcp_socket (host, port, family, error); + if (fd < 0) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + _dbus_string_free (&address); + return NULL; + } + + _dbus_fd_set_close_on_exec (fd); + + _dbus_verbose ("Successfully connected to tcp socket %s:%s\n", + host, port); + + transport = _dbus_transport_new_for_socket (fd, NULL, &address); + if (transport == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + _dbus_close_socket (fd, NULL); + _dbus_string_free (&address); + fd = -1; + } + + _dbus_string_free (&address); + + return transport; + +error: + _dbus_string_free (&address); + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return NULL; +} + +/** + * Opens a TCP socket transport. + * + * @param entry the address entry to try opening as a tcp transport. + * @param transport_p return location for the opened transport + * @param error error to be set + * @returns result of the attempt + */ +DBusTransportOpenResult +_dbus_transport_open_socket(DBusAddressEntry *entry, + DBusTransport **transport_p, + DBusError *error) +{ + const char *method; + + method = dbus_address_entry_get_method (entry); + _dbus_assert (method != NULL); + + if (strcmp (method, "tcp") == 0) + { + const char *host = dbus_address_entry_get_value (entry, "host"); + const char *port = dbus_address_entry_get_value (entry, "port"); + const char *family = dbus_address_entry_get_value (entry, "family"); + + if (port == NULL) + { + _dbus_set_bad_address (error, "tcp", "port", NULL); + return DBUS_TRANSPORT_OPEN_BAD_ADDRESS; + } + + *transport_p = _dbus_transport_new_for_tcp_socket (host, port, family, error); + if (*transport_p == NULL) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT; + } + else + { + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + return DBUS_TRANSPORT_OPEN_OK; + } + } + else + { + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + return DBUS_TRANSPORT_OPEN_NOT_HANDLED; + } +} + +/** @} */ + diff --git a/src/dbus/dbus-transport-socket.h b/src/dbus/dbus-transport-socket.h new file mode 100644 index 0000000..8a00ab5 --- /dev/null +++ b/src/dbus/dbus-transport-socket.h @@ -0,0 +1,45 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-transport-socket.h Socket subclasses of DBusTransport + * + * Copyright (C) 2002, 2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef DBUS_TRANSPORT_SOCKET_H +#define DBUS_TRANSPORT_SOCKET_H + +#include + +DBUS_BEGIN_DECLS + +DBusTransport* _dbus_transport_new_for_socket (int fd, + const DBusString *server_guid, + const DBusString *address); +DBusTransport* _dbus_transport_new_for_tcp_socket (const char *host, + const char *port, + const char *family, + DBusError *error); +DBusTransportOpenResult _dbus_transport_open_socket (DBusAddressEntry *entry, + DBusTransport **transport_p, + DBusError *error); + + + +DBUS_END_DECLS + +#endif /* DBUS_TRANSPORT_SOCKET_H */ diff --git a/src/dbus/dbus-transport-unix.c b/src/dbus/dbus-transport-unix.c new file mode 100644 index 0000000..a13fde1 --- /dev/null +++ b/src/dbus/dbus-transport-unix.c @@ -0,0 +1,181 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-transport-unix.c UNIX socket subclasses of DBusTransport + * + * Copyright (C) 2002, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-internals.h" +#include "dbus-connection-internal.h" +#include "dbus-transport-unix.h" +#include "dbus-transport-socket.h" +#include "dbus-transport-protected.h" +#include "dbus-watch.h" +#include "dbus-sysdeps-unix.h" + +/** + * @defgroup DBusTransportUnix DBusTransport implementations for UNIX + * @ingroup DBusInternals + * @brief Implementation details of DBusTransport on UNIX + * + * @{ + */ + +/** + * Creates a new transport for the given Unix domain socket + * path. This creates a client-side of a transport. + * + * @todo once we add a way to escape paths in a dbus + * address, this function needs to do escaping. + * + * @param path the path to the domain socket. + * @param abstract #TRUE to use abstract socket namespace + * @param error address where an error can be returned. + * @returns a new transport, or #NULL on failure. + */ +DBusTransport* +_dbus_transport_new_for_domain_socket (const char *path, + dbus_bool_t abstract, + DBusError *error) +{ + int fd; + DBusTransport *transport; + DBusString address; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + if (!_dbus_string_init (&address)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return NULL; + } + + fd = -1; + + if ((abstract && + !_dbus_string_append (&address, "unix:abstract=")) || + (!abstract && + !_dbus_string_append (&address, "unix:path=")) || + !_dbus_string_append (&address, path)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto failed_0; + } + + fd = _dbus_connect_unix_socket (path, abstract, error); + if (fd < 0) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + goto failed_0; + } + + _dbus_fd_set_close_on_exec (fd); + + _dbus_verbose ("Successfully connected to unix socket %s\n", + path); + + transport = _dbus_transport_new_for_socket (fd, NULL, &address); + if (transport == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto failed_1; + } + + _dbus_string_free (&address); + + return transport; + + failed_1: + _dbus_close_socket (fd, NULL); + failed_0: + _dbus_string_free (&address); + return NULL; +} + +/** + * Opens platform specific transport types. + * + * @param entry the address entry to try opening + * @param transport_p return location for the opened transport + * @param error error to be set + * @returns result of the attempt + */ +DBusTransportOpenResult +_dbus_transport_open_platform_specific (DBusAddressEntry *entry, + DBusTransport **transport_p, + DBusError *error) +{ + const char *method; + + method = dbus_address_entry_get_method (entry); + _dbus_assert (method != NULL); + + if (strcmp (method, "unix") == 0) + { + const char *path = dbus_address_entry_get_value (entry, "path"); + const char *tmpdir = dbus_address_entry_get_value (entry, "tmpdir"); + const char *abstract = dbus_address_entry_get_value (entry, "abstract"); + + if (tmpdir != NULL) + { + _dbus_set_bad_address (error, NULL, NULL, + "cannot use the \"tmpdir\" option for an address to connect to, only in an address to listen on"); + return DBUS_TRANSPORT_OPEN_BAD_ADDRESS; + } + + if (path == NULL && abstract == NULL) + { + _dbus_set_bad_address (error, "unix", + "path or abstract", + NULL); + return DBUS_TRANSPORT_OPEN_BAD_ADDRESS; + } + + if (path != NULL && abstract != NULL) + { + _dbus_set_bad_address (error, NULL, NULL, + "can't specify both \"path\" and \"abstract\" options in an address"); + return DBUS_TRANSPORT_OPEN_BAD_ADDRESS; + } + + if (path) + *transport_p = _dbus_transport_new_for_domain_socket (path, FALSE, + error); + else + *transport_p = _dbus_transport_new_for_domain_socket (abstract, TRUE, + error); + if (*transport_p == NULL) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT; + } + else + { + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + return DBUS_TRANSPORT_OPEN_OK; + } + } + else + { + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + return DBUS_TRANSPORT_OPEN_NOT_HANDLED; + } +} + +/** @} */ diff --git a/src/dbus/dbus-transport-unix.h b/src/dbus/dbus-transport-unix.h new file mode 100644 index 0000000..3807ed2 --- /dev/null +++ b/src/dbus/dbus-transport-unix.h @@ -0,0 +1,37 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-transport-unix.h UNIX socket subclasses of DBusTransport + * + * Copyright (C) 2002 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef DBUS_TRANSPORT_UNIX_H +#define DBUS_TRANSPORT_UNIX_H + +#include + +DBUS_BEGIN_DECLS + +DBusTransport* _dbus_transport_new_for_domain_socket (const char *path, + dbus_bool_t abstract, + DBusError *error); + + +DBUS_END_DECLS + +#endif /* DBUS_TRANSPORT_UNIX_H */ diff --git a/src/dbus/dbus-transport.c b/src/dbus/dbus-transport.c new file mode 100644 index 0000000..35b7027 --- /dev/null +++ b/src/dbus/dbus-transport.c @@ -0,0 +1,1411 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-transport.c DBusTransport object (internal to D-Bus implementation) + * + * Copyright (C) 2002, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-transport-protected.h" +#include "dbus-transport-unix.h" +#include "dbus-transport-socket.h" +#include "dbus-connection-internal.h" +#include "dbus-watch.h" +#include "dbus-auth.h" +#include "dbus-address.h" +#include "dbus-credentials.h" +#ifdef DBUS_BUILD_TESTS +#include "dbus-server-debug-pipe.h" +#endif + +/** + * @defgroup DBusTransport DBusTransport object + * @ingroup DBusInternals + * @brief "Backend" for a DBusConnection. + * + * Types and functions related to DBusTransport. A transport is an + * abstraction that can send and receive data via various kinds of + * network connections or other IPC mechanisms. + * + * @{ + */ + +/** + * @typedef DBusTransport + * + * Opaque object representing a way message stream. + * DBusTransport abstracts various kinds of actual + * transport mechanism, such as different network protocols, + * or encryption schemes. + */ + +static void +live_messages_size_notify (DBusCounter *counter, + void *user_data) +{ + DBusTransport *transport = user_data; + + _dbus_transport_ref (transport); + +#if 0 + _dbus_verbose ("Counter value is now %d\n", + (int) _dbus_counter_get_value (counter)); +#endif + + /* disable or re-enable the read watch for the transport if + * required. + */ + if (transport->vtable->live_messages_changed) + (* transport->vtable->live_messages_changed) (transport); + + _dbus_transport_unref (transport); +} + +/** + * Initializes the base class members of DBusTransport. Chained up to + * by subclasses in their constructor. The server GUID is the + * globally unique ID for the server creating this connection + * and will be #NULL for the client side of a connection. The GUID + * is in hex format. + * + * @param transport the transport being created. + * @param vtable the subclass vtable. + * @param server_guid non-#NULL if this transport is on the server side of a connection + * @param address the address of the transport + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_transport_init_base (DBusTransport *transport, + const DBusTransportVTable *vtable, + const DBusString *server_guid, + const DBusString *address) +{ + DBusMessageLoader *loader; + DBusAuth *auth; + DBusCounter *counter; + char *address_copy; + DBusCredentials *creds; + + loader = _dbus_message_loader_new (); + if (loader == NULL) + return FALSE; + + if (server_guid) + auth = _dbus_auth_server_new (server_guid); + else + auth = _dbus_auth_client_new (); + if (auth == NULL) + { + _dbus_message_loader_unref (loader); + return FALSE; + } + + counter = _dbus_counter_new (); + if (counter == NULL) + { + _dbus_auth_unref (auth); + _dbus_message_loader_unref (loader); + return FALSE; + } + + creds = _dbus_credentials_new (); + if (creds == NULL) + { + _dbus_counter_unref (counter); + _dbus_auth_unref (auth); + _dbus_message_loader_unref (loader); + return FALSE; + } + + if (server_guid) + { + _dbus_assert (address == NULL); + address_copy = NULL; + } + else + { + _dbus_assert (address != NULL); + + if (!_dbus_string_copy_data (address, &address_copy)) + { + _dbus_credentials_unref (creds); + _dbus_counter_unref (counter); + _dbus_auth_unref (auth); + _dbus_message_loader_unref (loader); + return FALSE; + } + } + + transport->refcount = 1; + transport->vtable = vtable; + transport->loader = loader; + transport->auth = auth; + transport->live_messages_size = counter; + transport->authenticated = FALSE; + transport->disconnected = FALSE; + transport->is_server = (server_guid != NULL); + transport->send_credentials_pending = !transport->is_server; + transport->receive_credentials_pending = transport->is_server; + transport->address = address_copy; + + transport->unix_user_function = NULL; + transport->unix_user_data = NULL; + transport->free_unix_user_data = NULL; + + transport->windows_user_function = NULL; + transport->windows_user_data = NULL; + transport->free_windows_user_data = NULL; + + transport->expected_guid = NULL; + + /* Try to default to something that won't totally hose the system, + * but doesn't impose too much of a limitation. + */ + transport->max_live_messages_size = _DBUS_ONE_MEGABYTE * 63; + + /* credentials read from socket if any */ + transport->credentials = creds; + + _dbus_counter_set_notify (transport->live_messages_size, + transport->max_live_messages_size, + live_messages_size_notify, + transport); + + if (transport->address) + _dbus_verbose ("Initialized transport on address %s\n", transport->address); + + return TRUE; +} + +/** + * Finalizes base class members of DBusTransport. + * Chained up to from subclass finalizers. + * + * @param transport the transport. + */ +void +_dbus_transport_finalize_base (DBusTransport *transport) +{ + if (!transport->disconnected) + _dbus_transport_disconnect (transport); + + if (transport->free_unix_user_data != NULL) + (* transport->free_unix_user_data) (transport->unix_user_data); + + if (transport->free_windows_user_data != NULL) + (* transport->free_windows_user_data) (transport->windows_user_data); + + _dbus_message_loader_unref (transport->loader); + _dbus_auth_unref (transport->auth); + _dbus_counter_set_notify (transport->live_messages_size, + 0, NULL, NULL); + _dbus_counter_unref (transport->live_messages_size); + dbus_free (transport->address); + dbus_free (transport->expected_guid); + if (transport->credentials) + _dbus_credentials_unref (transport->credentials); +} + + +/** + * Verifies if a given D-Bus address is a valid address + * by attempting to connect to it. If it is, returns the + * opened DBusTransport object. If it isn't, returns #NULL + * and sets @p error. + * + * @param error address where an error can be returned. + * @returns a new transport, or #NULL on failure. + */ +static DBusTransport* +check_address (const char *address, DBusError *error) +{ + DBusAddressEntry **entries; + DBusTransport *transport = NULL; + int len, i; + + _dbus_assert (address != NULL); + _dbus_assert (*address != '\0'); + + if (!dbus_parse_address (address, &entries, &len, error)) + return NULL; /* not a valid address */ + + for (i = 0; i < len; i++) + { + transport = _dbus_transport_open (entries[i], error); + if (transport != NULL) + break; + } + + dbus_address_entries_free (entries); + return transport; +} + +/** + * Creates a new transport for the "autostart" method. + * This creates a client-side of a transport. + * + * @param error address where an error can be returned. + * @returns a new transport, or #NULL on failure. + */ +static DBusTransport* +_dbus_transport_new_for_autolaunch (DBusError *error) +{ + DBusString address; + DBusTransport *result = NULL; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + if (!_dbus_string_init (&address)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return NULL; + } + + if (!_dbus_get_autolaunch_address (&address, error)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + goto out; + } + + result = check_address (_dbus_string_get_const_data (&address), error); + if (result == NULL) + _DBUS_ASSERT_ERROR_IS_SET (error); + else + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + out: + _dbus_string_free (&address); + return result; +} + +static DBusTransportOpenResult +_dbus_transport_open_autolaunch (DBusAddressEntry *entry, + DBusTransport **transport_p, + DBusError *error) +{ + const char *method; + + method = dbus_address_entry_get_method (entry); + _dbus_assert (method != NULL); + + if (strcmp (method, "autolaunch") == 0) + { + *transport_p = _dbus_transport_new_for_autolaunch (error); + + if (*transport_p == NULL) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT; + } + else + { + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + return DBUS_TRANSPORT_OPEN_OK; + } + } + else + { + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + return DBUS_TRANSPORT_OPEN_NOT_HANDLED; + } +} + +static const struct { + DBusTransportOpenResult (* func) (DBusAddressEntry *entry, + DBusTransport **transport_p, + DBusError *error); +} open_funcs[] = { + { _dbus_transport_open_socket }, + { _dbus_transport_open_platform_specific }, + { _dbus_transport_open_autolaunch } +#ifdef DBUS_BUILD_TESTS + , { _dbus_transport_open_debug_pipe } +#endif +}; + +/** + * Try to open a new transport for the given address entry. (This + * opens a client-side-of-the-connection transport.) + * + * @param entry the address entry + * @param error location to store reason for failure. + * @returns new transport of #NULL on failure. + */ +DBusTransport* +_dbus_transport_open (DBusAddressEntry *entry, + DBusError *error) +{ + DBusTransport *transport; + const char *expected_guid_orig; + char *expected_guid; + int i; + DBusError tmp_error = DBUS_ERROR_INIT; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + transport = NULL; + expected_guid_orig = dbus_address_entry_get_value (entry, "guid"); + expected_guid = _dbus_strdup (expected_guid_orig); + + if (expected_guid_orig != NULL && expected_guid == NULL) + { + _DBUS_SET_OOM (error); + return NULL; + } + + for (i = 0; i < (int) _DBUS_N_ELEMENTS (open_funcs); ++i) + { + DBusTransportOpenResult result; + + _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error); + result = (* open_funcs[i].func) (entry, &transport, &tmp_error); + + switch (result) + { + case DBUS_TRANSPORT_OPEN_OK: + _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error); + goto out; + break; + case DBUS_TRANSPORT_OPEN_NOT_HANDLED: + _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error); + /* keep going through the loop of open funcs */ + break; + case DBUS_TRANSPORT_OPEN_BAD_ADDRESS: + _DBUS_ASSERT_ERROR_IS_SET (&tmp_error); + goto out; + break; + case DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT: + _DBUS_ASSERT_ERROR_IS_SET (&tmp_error); + goto out; + break; + } + } + + out: + + if (transport == NULL) + { + if (!dbus_error_is_set (&tmp_error)) + _dbus_set_bad_address (&tmp_error, + NULL, NULL, + "Unknown address type (examples of valid types are \"tcp\" and on UNIX \"unix\")"); + + _DBUS_ASSERT_ERROR_IS_SET (&tmp_error); + dbus_move_error(&tmp_error, error); + dbus_free (expected_guid); + } + else + { + _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error); + + /* In the case of autostart the initial guid is NULL + * and the autostart transport recursively calls + * _dbus_open_transport wich returns a transport + * with a guid. That guid is the definitive one. + * + * FIXME: if more transports are added they may have + * an effect on the expected_guid semantics (i.e. + * expected_guid and transport->expected_guid may + * both have values). This is very unlikely though + * we should either throw asserts here for those + * corner cases or refactor the code so it is + * clearer on what is expected and what is not + */ + if(expected_guid) + transport->expected_guid = expected_guid; + } + + return transport; +} + +/** + * Increments the reference count for the transport. + * + * @param transport the transport. + * @returns the transport. + */ +DBusTransport * +_dbus_transport_ref (DBusTransport *transport) +{ + _dbus_assert (transport->refcount > 0); + + transport->refcount += 1; + + return transport; +} + +/** + * Decrements the reference count for the transport. + * Disconnects and finalizes the transport if + * the reference count reaches zero. + * + * @param transport the transport. + */ +void +_dbus_transport_unref (DBusTransport *transport) +{ + _dbus_assert (transport != NULL); + _dbus_assert (transport->refcount > 0); + + transport->refcount -= 1; + if (transport->refcount == 0) + { + _dbus_verbose ("%s: finalizing\n", _DBUS_FUNCTION_NAME); + + _dbus_assert (transport->vtable->finalize != NULL); + + (* transport->vtable->finalize) (transport); + } +} + +/** + * Closes our end of the connection to a remote application. Further + * attempts to use this transport will fail. Only the first call to + * _dbus_transport_disconnect() will have an effect. + * + * @param transport the transport. + * + */ +void +_dbus_transport_disconnect (DBusTransport *transport) +{ + _dbus_verbose ("%s start\n", _DBUS_FUNCTION_NAME); + + _dbus_assert (transport->vtable->disconnect != NULL); + + if (transport->disconnected) + return; + + (* transport->vtable->disconnect) (transport); + + transport->disconnected = TRUE; + + _dbus_verbose ("%s end\n", _DBUS_FUNCTION_NAME); +} + +/** + * Returns #TRUE if the transport has not been disconnected. + * Disconnection can result from _dbus_transport_disconnect() + * or because the server drops its end of the connection. + * + * @param transport the transport. + * @returns whether we're connected + */ +dbus_bool_t +_dbus_transport_get_is_connected (DBusTransport *transport) +{ + return !transport->disconnected; +} + +static dbus_bool_t +auth_via_unix_user_function (DBusTransport *transport) +{ + DBusCredentials *auth_identity; + dbus_bool_t allow; + DBusConnection *connection; + DBusAllowUnixUserFunction unix_user_function; + void *unix_user_data; + dbus_uid_t uid; + + /* Dropping the lock here probably isn't that safe. */ + + auth_identity = _dbus_auth_get_identity (transport->auth); + _dbus_assert (auth_identity != NULL); + + connection = transport->connection; + unix_user_function = transport->unix_user_function; + unix_user_data = transport->unix_user_data; + uid = _dbus_credentials_get_unix_uid (auth_identity); + + _dbus_verbose ("unlock %s\n", _DBUS_FUNCTION_NAME); + _dbus_connection_unlock (connection); + + allow = (* unix_user_function) (connection, + uid, + unix_user_data); + + _dbus_verbose ("lock %s post unix user function\n", _DBUS_FUNCTION_NAME); + _dbus_connection_lock (connection); + + if (allow) + { + _dbus_verbose ("Client UID "DBUS_UID_FORMAT" authorized\n", uid); + } + else + { + _dbus_verbose ("Client UID "DBUS_UID_FORMAT + " was rejected, disconnecting\n", + _dbus_credentials_get_unix_uid (auth_identity)); + _dbus_transport_disconnect (transport); + } + + return allow; +} + +static dbus_bool_t +auth_via_windows_user_function (DBusTransport *transport) +{ + DBusCredentials *auth_identity; + dbus_bool_t allow; + DBusConnection *connection; + DBusAllowWindowsUserFunction windows_user_function; + void *windows_user_data; + char *windows_sid; + + /* Dropping the lock here probably isn't that safe. */ + + auth_identity = _dbus_auth_get_identity (transport->auth); + _dbus_assert (auth_identity != NULL); + + connection = transport->connection; + windows_user_function = transport->windows_user_function; + windows_user_data = transport->unix_user_data; + windows_sid = _dbus_strdup (_dbus_credentials_get_windows_sid (auth_identity)); + + if (windows_sid == NULL) + { + /* OOM */ + return FALSE; + } + + _dbus_verbose ("unlock %s\n", _DBUS_FUNCTION_NAME); + _dbus_connection_unlock (connection); + + allow = (* windows_user_function) (connection, + windows_sid, + windows_user_data); + + _dbus_verbose ("lock %s post windows user function\n", _DBUS_FUNCTION_NAME); + _dbus_connection_lock (connection); + + if (allow) + { + _dbus_verbose ("Client SID '%s' authorized\n", windows_sid); + } + else + { + _dbus_verbose ("Client SID '%s' was rejected, disconnecting\n", + _dbus_credentials_get_windows_sid (auth_identity)); + _dbus_transport_disconnect (transport); + } + + return allow; +} + +static dbus_bool_t +auth_via_default_rules (DBusTransport *transport) +{ + DBusCredentials *auth_identity; + DBusCredentials *our_identity; + dbus_bool_t allow; + + auth_identity = _dbus_auth_get_identity (transport->auth); + _dbus_assert (auth_identity != NULL); + + /* By default, connection is allowed if the client is 1) root or 2) + * has the same UID as us or 3) anonymous is allowed. + */ + + our_identity = _dbus_credentials_new_from_current_process (); + if (our_identity == NULL) + { + /* OOM */ + return FALSE; + } + + if (transport->allow_anonymous || + _dbus_credentials_get_unix_uid (auth_identity) == 0 || + _dbus_credentials_same_user (our_identity, + auth_identity)) + { + if (_dbus_credentials_include(our_identity,DBUS_CREDENTIAL_WINDOWS_SID)) + _dbus_verbose ("Client authorized as SID '%s'" + "matching our SID '%s'\n", + _dbus_credentials_get_windows_sid(auth_identity), + _dbus_credentials_get_windows_sid(our_identity)); + else + _dbus_verbose ("Client authorized as UID "DBUS_UID_FORMAT + " matching our UID "DBUS_UID_FORMAT"\n", + _dbus_credentials_get_unix_uid(auth_identity), + _dbus_credentials_get_unix_uid(our_identity)); + /* We have authenticated! */ + allow = TRUE; + } + else + { + if (_dbus_credentials_include(our_identity,DBUS_CREDENTIAL_WINDOWS_SID)) + _dbus_verbose ("Client authorized as SID '%s'" + " but our SID is '%s', disconnecting\n", + _dbus_credentials_get_windows_sid(our_identity), + _dbus_credentials_get_windows_sid(our_identity)); + else + _dbus_verbose ("Client authorized as UID "DBUS_UID_FORMAT + " but our UID is "DBUS_UID_FORMAT", disconnecting\n", + _dbus_credentials_get_unix_uid(our_identity), + _dbus_credentials_get_unix_uid(our_identity)); + _dbus_transport_disconnect (transport); + allow = FALSE; + } + + _dbus_credentials_unref (our_identity); + + return allow; +} + + +/** + * Returns #TRUE if we have been authenticated. Will return #TRUE + * even if the transport is disconnected. + * + * @todo we drop connection->mutex when calling the unix_user_function, + * and windows_user_function, which may not be safe really. + * + * @param transport the transport + * @returns whether we're authenticated + */ +dbus_bool_t +_dbus_transport_get_is_authenticated (DBusTransport *transport) +{ + if (transport->authenticated) + return TRUE; + else + { + dbus_bool_t maybe_authenticated; + + if (transport->disconnected) + return FALSE; + + /* paranoia ref since we call user callbacks sometimes */ + _dbus_connection_ref_unlocked (transport->connection); + + maybe_authenticated = + (!(transport->send_credentials_pending || + transport->receive_credentials_pending)); + + if (maybe_authenticated) + { + switch (_dbus_auth_do_work (transport->auth)) + { + case DBUS_AUTH_STATE_AUTHENTICATED: + /* leave as maybe_authenticated */ + break; + default: + maybe_authenticated = FALSE; + } + } + + /* If we're the client, verify the GUID + */ + if (maybe_authenticated && !transport->is_server) + { + const char *server_guid; + + server_guid = _dbus_auth_get_guid_from_server (transport->auth); + _dbus_assert (server_guid != NULL); + + if (transport->expected_guid && + strcmp (transport->expected_guid, server_guid) != 0) + { + _dbus_verbose ("Client expected GUID '%s' and we got '%s' from the server\n", + transport->expected_guid, server_guid); + _dbus_transport_disconnect (transport); + _dbus_connection_unref_unlocked (transport->connection); + return FALSE; + } + + if (transport->expected_guid == NULL) + { + transport->expected_guid = _dbus_strdup (server_guid); + + if (transport->expected_guid == NULL) + { + _dbus_verbose ("No memory to complete auth in %s\n", _DBUS_FUNCTION_NAME); + return FALSE; + } + } + } + + /* If we're the server, see if we want to allow this identity to proceed. + */ + if (maybe_authenticated && transport->is_server) + { + dbus_bool_t allow; + DBusCredentials *auth_identity; + + auth_identity = _dbus_auth_get_identity (transport->auth); + _dbus_assert (auth_identity != NULL); + + /* If we have an auth'd user and a user function, delegate + * deciding whether auth credentials are good enough to the + * app; otherwise, use our default decision process. + */ + if (transport->unix_user_function != NULL && + _dbus_credentials_include (auth_identity, DBUS_CREDENTIAL_UNIX_USER_ID)) + { + allow = auth_via_unix_user_function (transport); + } + else if (transport->windows_user_function != NULL && + _dbus_credentials_include (auth_identity, DBUS_CREDENTIAL_WINDOWS_SID)) + { + allow = auth_via_windows_user_function (transport); + } + else + { + allow = auth_via_default_rules (transport); + } + + if (!allow) + maybe_authenticated = FALSE; + } + + transport->authenticated = maybe_authenticated; + + _dbus_connection_unref_unlocked (transport->connection); + return maybe_authenticated; + } +} + +/** + * See dbus_connection_get_is_anonymous(). + * + * @param transport the transport + * @returns #TRUE if not authenticated or authenticated as anonymous + */ +dbus_bool_t +_dbus_transport_get_is_anonymous (DBusTransport *transport) +{ + DBusCredentials *auth_identity; + + if (!transport->authenticated) + return TRUE; + + auth_identity = _dbus_auth_get_identity (transport->auth); + + if (_dbus_credentials_are_anonymous (auth_identity)) + return TRUE; + else + return FALSE; +} + +/** + * Gets the address of a transport. It will be + * #NULL for a server-side transport. + * + * @param transport the transport + * @returns transport's address + */ +const char* +_dbus_transport_get_address (DBusTransport *transport) +{ + return transport->address; +} + +/** + * Gets the id of the server we are connected to (see + * dbus_server_get_id()). Only works on client side. + * + * @param transport the transport + * @returns transport's server's id or #NULL if we are the server side + */ +const char* +_dbus_transport_get_server_id (DBusTransport *transport) +{ + if (transport->is_server) + return NULL; + else + return transport->expected_guid; +} + +/** + * Handles a watch by reading data, writing data, or disconnecting + * the transport, as appropriate for the given condition. + * + * @param transport the transport. + * @param watch the watch. + * @param condition the current state of the watched file descriptor. + * @returns #FALSE if not enough memory to fully handle the watch + */ +dbus_bool_t +_dbus_transport_handle_watch (DBusTransport *transport, + DBusWatch *watch, + unsigned int condition) +{ + dbus_bool_t retval; + + _dbus_assert (transport->vtable->handle_watch != NULL); + + if (transport->disconnected) + return TRUE; + + if (dbus_watch_get_socket (watch) < 0) + { + _dbus_warn_check_failed ("Tried to handle an invalidated watch; this watch should have been removed\n"); + return TRUE; + } + + _dbus_watch_sanitize_condition (watch, &condition); + + _dbus_transport_ref (transport); + _dbus_watch_ref (watch); + retval = (* transport->vtable->handle_watch) (transport, watch, condition); + _dbus_watch_unref (watch); + _dbus_transport_unref (transport); + + return retval; +} + +/** + * Sets the connection using this transport. Allows the transport + * to add watches to the connection, queue incoming messages, + * and pull outgoing messages. + * + * @param transport the transport. + * @param connection the connection. + * @returns #FALSE if not enough memory + */ +dbus_bool_t +_dbus_transport_set_connection (DBusTransport *transport, + DBusConnection *connection) +{ + _dbus_assert (transport->vtable->connection_set != NULL); + _dbus_assert (transport->connection == NULL); + + transport->connection = connection; + + _dbus_transport_ref (transport); + if (!(* transport->vtable->connection_set) (transport)) + transport->connection = NULL; + _dbus_transport_unref (transport); + + return transport->connection != NULL; +} + +/** + * Get the socket file descriptor, if any. + * + * @param transport the transport + * @param fd_p pointer to fill in with the descriptor + * @returns #TRUE if a descriptor was available + */ +dbus_bool_t +_dbus_transport_get_socket_fd (DBusTransport *transport, + int *fd_p) +{ + dbus_bool_t retval; + + if (transport->vtable->get_socket_fd == NULL) + return FALSE; + + if (transport->disconnected) + return FALSE; + + _dbus_transport_ref (transport); + + retval = (* transport->vtable->get_socket_fd) (transport, + fd_p); + + _dbus_transport_unref (transport); + + return retval; +} + +/** + * Performs a single poll()/select() on the transport's file + * descriptors and then reads/writes data as appropriate, + * queueing incoming messages and sending outgoing messages. + * This is the backend for _dbus_connection_do_iteration(). + * See _dbus_connection_do_iteration() for full details. + * + * @param transport the transport. + * @param flags indicates whether to read or write, and whether to block. + * @param timeout_milliseconds if blocking, timeout or -1 for no timeout. + */ +void +_dbus_transport_do_iteration (DBusTransport *transport, + unsigned int flags, + int timeout_milliseconds) +{ + _dbus_assert (transport->vtable->do_iteration != NULL); + + _dbus_verbose ("Transport iteration flags 0x%x timeout %d connected = %d\n", + flags, timeout_milliseconds, !transport->disconnected); + + if ((flags & (DBUS_ITERATION_DO_WRITING | + DBUS_ITERATION_DO_READING)) == 0) + return; /* Nothing to do */ + + if (transport->disconnected) + return; + + _dbus_transport_ref (transport); + (* transport->vtable->do_iteration) (transport, flags, + timeout_milliseconds); + _dbus_transport_unref (transport); + + _dbus_verbose ("%s end\n", _DBUS_FUNCTION_NAME); +} + +static dbus_bool_t +recover_unused_bytes (DBusTransport *transport) +{ + if (_dbus_auth_needs_decoding (transport->auth)) + { + DBusString plaintext; + const DBusString *encoded; + DBusString *buffer; + int orig_len; + + if (!_dbus_string_init (&plaintext)) + goto nomem; + + _dbus_auth_get_unused_bytes (transport->auth, + &encoded); + + if (!_dbus_auth_decode_data (transport->auth, + encoded, &plaintext)) + { + _dbus_string_free (&plaintext); + goto nomem; + } + + _dbus_message_loader_get_buffer (transport->loader, + &buffer); + + orig_len = _dbus_string_get_length (buffer); + + if (!_dbus_string_move (&plaintext, 0, buffer, + orig_len)) + { + _dbus_string_free (&plaintext); + goto nomem; + } + + _dbus_verbose (" %d unused bytes sent to message loader\n", + _dbus_string_get_length (buffer) - + orig_len); + + _dbus_message_loader_return_buffer (transport->loader, + buffer, + _dbus_string_get_length (buffer) - + orig_len); + + _dbus_auth_delete_unused_bytes (transport->auth); + + _dbus_string_free (&plaintext); + } + else + { + const DBusString *bytes; + DBusString *buffer; + int orig_len; + dbus_bool_t succeeded; + + _dbus_message_loader_get_buffer (transport->loader, + &buffer); + + orig_len = _dbus_string_get_length (buffer); + + _dbus_auth_get_unused_bytes (transport->auth, + &bytes); + + succeeded = TRUE; + if (!_dbus_string_copy (bytes, 0, buffer, _dbus_string_get_length (buffer))) + succeeded = FALSE; + + _dbus_verbose (" %d unused bytes sent to message loader\n", + _dbus_string_get_length (buffer) - + orig_len); + + _dbus_message_loader_return_buffer (transport->loader, + buffer, + _dbus_string_get_length (buffer) - + orig_len); + + if (succeeded) + _dbus_auth_delete_unused_bytes (transport->auth); + else + goto nomem; + } + + return TRUE; + + nomem: + _dbus_verbose ("Not enough memory to transfer unused bytes from auth conversation\n"); + return FALSE; +} + +/** + * Reports our current dispatch status (whether there's buffered + * data to be queued as messages, or not, or we need memory). + * + * @param transport the transport + * @returns current status + */ +DBusDispatchStatus +_dbus_transport_get_dispatch_status (DBusTransport *transport) +{ + if (_dbus_counter_get_value (transport->live_messages_size) >= transport->max_live_messages_size) + return DBUS_DISPATCH_COMPLETE; /* complete for now */ + + if (!_dbus_transport_get_is_authenticated (transport)) + { + if (_dbus_auth_do_work (transport->auth) == + DBUS_AUTH_STATE_WAITING_FOR_MEMORY) + return DBUS_DISPATCH_NEED_MEMORY; + else if (!_dbus_transport_get_is_authenticated (transport)) + return DBUS_DISPATCH_COMPLETE; + } + + if (!transport->unused_bytes_recovered && + !recover_unused_bytes (transport)) + return DBUS_DISPATCH_NEED_MEMORY; + + transport->unused_bytes_recovered = TRUE; + + if (!_dbus_message_loader_queue_messages (transport->loader)) + return DBUS_DISPATCH_NEED_MEMORY; + + if (_dbus_message_loader_peek_message (transport->loader) != NULL) + return DBUS_DISPATCH_DATA_REMAINS; + else + return DBUS_DISPATCH_COMPLETE; +} + +/** + * Processes data we've read while handling a watch, potentially + * converting some of it to messages and queueing those messages on + * the connection. + * + * @param transport the transport + * @returns #TRUE if we had enough memory to queue all messages + */ +dbus_bool_t +_dbus_transport_queue_messages (DBusTransport *transport) +{ + DBusDispatchStatus status; + +#if 0 + _dbus_verbose ("_dbus_transport_queue_messages()\n"); +#endif + + /* Queue any messages */ + while ((status = _dbus_transport_get_dispatch_status (transport)) == DBUS_DISPATCH_DATA_REMAINS) + { + DBusMessage *message; + DBusList *link; + + link = _dbus_message_loader_pop_message_link (transport->loader); + _dbus_assert (link != NULL); + + message = link->data; + + _dbus_verbose ("queueing received message %p\n", message); + + if (!_dbus_message_add_size_counter (message, transport->live_messages_size)) + { + _dbus_message_loader_putback_message_link (transport->loader, + link); + status = DBUS_DISPATCH_NEED_MEMORY; + break; + } + else + { + /* pass ownership of link and message ref to connection */ + _dbus_connection_queue_received_message_link (transport->connection, + link); + } + } + + if (_dbus_message_loader_get_is_corrupted (transport->loader)) + { + _dbus_verbose ("Corrupted message stream, disconnecting\n"); + _dbus_transport_disconnect (transport); + } + + return status != DBUS_DISPATCH_NEED_MEMORY; +} + +/** + * See dbus_connection_set_max_message_size(). + * + * @param transport the transport + * @param size the max size of a single message + */ +void +_dbus_transport_set_max_message_size (DBusTransport *transport, + long size) +{ + _dbus_message_loader_set_max_message_size (transport->loader, size); +} + +/** + * See dbus_connection_get_max_message_size(). + * + * @param transport the transport + * @returns max message size + */ +long +_dbus_transport_get_max_message_size (DBusTransport *transport) +{ + return _dbus_message_loader_get_max_message_size (transport->loader); +} + +/** + * See dbus_connection_set_max_received_size(). + * + * @param transport the transport + * @param size the max size of all incoming messages + */ +void +_dbus_transport_set_max_received_size (DBusTransport *transport, + long size) +{ + transport->max_live_messages_size = size; + _dbus_counter_set_notify (transport->live_messages_size, + transport->max_live_messages_size, + live_messages_size_notify, + transport); +} + + +/** + * See dbus_connection_get_max_received_size(). + * + * @param transport the transport + * @returns max bytes for all live messages + */ +long +_dbus_transport_get_max_received_size (DBusTransport *transport) +{ + return transport->max_live_messages_size; +} + +/** + * See dbus_connection_get_unix_user(). + * + * @param transport the transport + * @param uid return location for the user ID + * @returns #TRUE if uid is filled in with a valid user ID + */ +dbus_bool_t +_dbus_transport_get_unix_user (DBusTransport *transport, + unsigned long *uid) +{ + DBusCredentials *auth_identity; + + *uid = _DBUS_INT32_MAX; /* better than some root or system user in + * case of bugs in the caller. Caller should + * never use this value on purpose, however. + */ + + if (!transport->authenticated) + return FALSE; + + auth_identity = _dbus_auth_get_identity (transport->auth); + + if (_dbus_credentials_include (auth_identity, + DBUS_CREDENTIAL_UNIX_USER_ID)) + { + *uid = _dbus_credentials_get_unix_uid (auth_identity); + return TRUE; + } + else + return FALSE; +} + +/** + * See dbus_connection_get_unix_process_id(). + * + * @param transport the transport + * @param pid return location for the process ID + * @returns #TRUE if uid is filled in with a valid process ID + */ +dbus_bool_t +_dbus_transport_get_unix_process_id (DBusTransport *transport, + unsigned long *pid) +{ + DBusCredentials *auth_identity; + + *pid = DBUS_PID_UNSET; /* Caller should never use this value on purpose, + * but we set it to a safe number, INT_MAX, + * just to root out possible bugs in bad callers. + */ + + if (!transport->authenticated) + return FALSE; + + auth_identity = _dbus_auth_get_identity (transport->auth); + + if (_dbus_credentials_include (auth_identity, + DBUS_CREDENTIAL_UNIX_PROCESS_ID)) + { + *pid = _dbus_credentials_get_unix_pid (auth_identity); + return TRUE; + } + else + return FALSE; +} + +/** + * See dbus_connection_get_adt_audit_session_data(). + * + * @param transport the transport + * @param data return location for the ADT audit data + * @param data_size return length of audit data + * @returns #TRUE if audit data is filled in with a valid ucred + */ +dbus_bool_t +_dbus_transport_get_adt_audit_session_data (DBusTransport *transport, + void **data, + int *data_size) +{ + DBusCredentials *auth_identity; + + *data = NULL; + *data_size = 0; + + if (!transport->authenticated) + return FALSE; + + auth_identity = _dbus_auth_get_identity (transport->auth); + + if (_dbus_credentials_include (auth_identity, + DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID)) + { + *data = (void *) _dbus_credentials_get_adt_audit_data (auth_identity); + *data_size = _dbus_credentials_get_adt_audit_data_size (auth_identity); + return TRUE; + } + else + return FALSE; +} + +/** + * See dbus_connection_set_unix_user_function(). + * + * @param transport the transport + * @param function the predicate + * @param data data to pass to the predicate + * @param free_data_function function to free the data + * @param old_data the old user data to be freed + * @param old_free_data_function old free data function to free it with + */ +void +_dbus_transport_set_unix_user_function (DBusTransport *transport, + DBusAllowUnixUserFunction function, + void *data, + DBusFreeFunction free_data_function, + void **old_data, + DBusFreeFunction *old_free_data_function) +{ + *old_data = transport->unix_user_data; + *old_free_data_function = transport->free_unix_user_data; + + transport->unix_user_function = function; + transport->unix_user_data = data; + transport->free_unix_user_data = free_data_function; +} + +/** + * See dbus_connection_get_windows_user(). + * + * @param transport the transport + * @param windows_sid_p return location for the user ID + * @returns #TRUE if user is available; the returned value may still be #NULL if no memory to copy it + */ +dbus_bool_t +_dbus_transport_get_windows_user (DBusTransport *transport, + char **windows_sid_p) +{ + DBusCredentials *auth_identity; + + *windows_sid_p = NULL; + + if (!transport->authenticated) + return FALSE; + + auth_identity = _dbus_auth_get_identity (transport->auth); + + if (_dbus_credentials_include (auth_identity, + DBUS_CREDENTIAL_WINDOWS_SID)) + { + /* If no memory, we are supposed to return TRUE and set NULL */ + *windows_sid_p = _dbus_strdup (_dbus_credentials_get_windows_sid (auth_identity)); + + return TRUE; + } + else + return FALSE; +} + +/** + * See dbus_connection_set_windows_user_function(). + * + * @param transport the transport + * @param function the predicate + * @param data data to pass to the predicate + * @param free_data_function function to free the data + * @param old_data the old user data to be freed + * @param old_free_data_function old free data function to free it with + */ + +void +_dbus_transport_set_windows_user_function (DBusTransport *transport, + DBusAllowWindowsUserFunction function, + void *data, + DBusFreeFunction free_data_function, + void **old_data, + DBusFreeFunction *old_free_data_function) +{ + *old_data = transport->windows_user_data; + *old_free_data_function = transport->free_windows_user_data; + + transport->windows_user_function = function; + transport->windows_user_data = data; + transport->free_windows_user_data = free_data_function; +} + +/** + * Sets the SASL authentication mechanisms supported by this transport. + * + * @param transport the transport + * @param mechanisms the #NULL-terminated array of mechanisms + * + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_transport_set_auth_mechanisms (DBusTransport *transport, + const char **mechanisms) +{ + return _dbus_auth_set_mechanisms (transport->auth, mechanisms); +} + +/** + * See dbus_connection_set_allow_anonymous() + * + * @param transport the transport + * @param value #TRUE to allow anonymous connection + */ +void +_dbus_transport_set_allow_anonymous (DBusTransport *transport, + dbus_bool_t value) +{ + transport->allow_anonymous = value != FALSE; +} + +/** @} */ diff --git a/src/dbus/dbus-transport.h b/src/dbus/dbus-transport.h new file mode 100644 index 0000000..691763c --- /dev/null +++ b/src/dbus/dbus-transport.h @@ -0,0 +1,92 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-transport.h DBusTransport object (internal to D-BUS implementation) + * + * Copyright (C) 2002, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef DBUS_TRANSPORT_H +#define DBUS_TRANSPORT_H + +#include +#include +#include +#include + +DBUS_BEGIN_DECLS + +typedef struct DBusTransport DBusTransport; + +DBusTransport* _dbus_transport_open (DBusAddressEntry *entry, + DBusError *error); +DBusTransport* _dbus_transport_ref (DBusTransport *transport); +void _dbus_transport_unref (DBusTransport *transport); +void _dbus_transport_disconnect (DBusTransport *transport); +dbus_bool_t _dbus_transport_get_is_connected (DBusTransport *transport); +dbus_bool_t _dbus_transport_get_is_authenticated (DBusTransport *transport); +dbus_bool_t _dbus_transport_get_is_anonymous (DBusTransport *transport); +const char* _dbus_transport_get_address (DBusTransport *transport); +const char* _dbus_transport_get_server_id (DBusTransport *transport); +dbus_bool_t _dbus_transport_handle_watch (DBusTransport *transport, + DBusWatch *watch, + unsigned int condition); +dbus_bool_t _dbus_transport_set_connection (DBusTransport *transport, + DBusConnection *connection); +void _dbus_transport_do_iteration (DBusTransport *transport, + unsigned int flags, + int timeout_milliseconds); +DBusDispatchStatus _dbus_transport_get_dispatch_status (DBusTransport *transport); +dbus_bool_t _dbus_transport_queue_messages (DBusTransport *transport); +void _dbus_transport_set_max_message_size (DBusTransport *transport, + long size); +long _dbus_transport_get_max_message_size (DBusTransport *transport); +void _dbus_transport_set_max_received_size (DBusTransport *transport, + long size); +long _dbus_transport_get_max_received_size (DBusTransport *transport); +dbus_bool_t _dbus_transport_get_socket_fd (DBusTransport *transport, + int *fd_p); +dbus_bool_t _dbus_transport_get_unix_user (DBusTransport *transport, + unsigned long *uid); +dbus_bool_t _dbus_transport_get_unix_process_id (DBusTransport *transport, + unsigned long *pid); +dbus_bool_t _dbus_transport_get_adt_audit_session_data (DBusTransport *transport, + void **data, + int *data_size); +void _dbus_transport_set_unix_user_function (DBusTransport *transport, + DBusAllowUnixUserFunction function, + void *data, + DBusFreeFunction free_data_function, + void **old_data, + DBusFreeFunction *old_free_data_function); +dbus_bool_t _dbus_transport_get_windows_user (DBusTransport *transport, + char **windows_sid_p); +void _dbus_transport_set_windows_user_function (DBusTransport *transport, + DBusAllowWindowsUserFunction function, + void *data, + DBusFreeFunction free_data_function, + void **old_data, + DBusFreeFunction *old_free_data_function); +dbus_bool_t _dbus_transport_set_auth_mechanisms (DBusTransport *transport, + const char **mechanisms); +void _dbus_transport_set_allow_anonymous (DBusTransport *transport, + dbus_bool_t value); + + +DBUS_END_DECLS + +#endif /* DBUS_TRANSPORT_H */ diff --git a/src/dbus/dbus-types.h b/src/dbus/dbus-types.h new file mode 100644 index 0000000..0a272d4 --- /dev/null +++ b/src/dbus/dbus-types.h @@ -0,0 +1,139 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-types.h types such as dbus_bool_t + * + * Copyright (C) 2002 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION) +#error "Only can be included directly, this file may disappear or change contents." +#endif + +#ifndef DBUS_TYPES_H +#define DBUS_TYPES_H + +#include +#include + +typedef dbus_uint32_t dbus_unichar_t; +/* boolean size must be fixed at 4 bytes due to wire protocol! */ +typedef dbus_uint32_t dbus_bool_t; + +/* Normally docs are in .c files, but there isn't a .c file for this. */ +/** + * @defgroup DBusTypes Basic types + * @ingroup DBus + * @brief dbus_bool_t, dbus_int32_t, etc. + * + * Typedefs for common primitive types. + * + * @{ + */ + +/** + * @typedef dbus_bool_t + * + * A boolean, valid values are #TRUE and #FALSE. + */ + +/** + * @typedef dbus_uint32_t + * + * A 32-bit unsigned integer on all platforms. + */ + +/** + * @typedef dbus_int32_t + * + * A 32-bit signed integer on all platforms. + */ + +/** + * @typedef dbus_uint16_t + * + * A 16-bit unsigned integer on all platforms. + */ + +/** + * @typedef dbus_int16_t + * + * A 16-bit signed integer on all platforms. + */ + + +/** + * @typedef dbus_uint64_t + * + * A 64-bit unsigned integer on all platforms that support it. + * If supported, #DBUS_HAVE_INT64 will be defined. + * + * C99 requires a 64-bit type and most likely all interesting + * compilers support one. GLib for example flat-out requires + * a 64-bit type. + * + * You probably want to just assume #DBUS_HAVE_INT64 is always defined. + */ + +/** + * @typedef dbus_int64_t + * + * A 64-bit signed integer on all platforms that support it. + * If supported, #DBUS_HAVE_INT64 will be defined. + * + * C99 requires a 64-bit type and most likely all interesting + * compilers support one. GLib for example flat-out requires + * a 64-bit type. + * + * You probably want to just assume #DBUS_HAVE_INT64 is always defined. + */ + +/** + * @def DBUS_HAVE_INT64 + * + * Defined if 64-bit integers are available. Will be defined + * on any platform you care about, unless you care about + * some truly ancient UNIX, or some bizarre embedded platform. + * + * C99 requires a 64-bit type and most likely all interesting + * compilers support one. GLib for example flat-out requires + * a 64-bit type. + * + * You should feel comfortable ignoring this macro and just using + * int64 unconditionally. + * + */ + +/** + * @def DBUS_INT64_CONSTANT + * + * Declare a 64-bit signed integer constant. The macro + * adds the necessary "LL" or whatever after the integer, + * giving a literal such as "325145246765LL" + */ + +/** + * @def DBUS_UINT64_CONSTANT + * + * Declare a 64-bit unsigned integer constant. The macro + * adds the necessary "ULL" or whatever after the integer, + * giving a literal such as "325145246765ULL" + */ + +/** @} */ + +#endif /* DBUS_TYPES_H */ diff --git a/src/dbus/dbus-userdb-util.c b/src/dbus/dbus-userdb-util.c new file mode 100644 index 0000000..d03a7c7 --- /dev/null +++ b/src/dbus/dbus-userdb-util.c @@ -0,0 +1,442 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-userdb-util.c Would be in dbus-userdb.c, but not used in libdbus + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#define DBUS_USERDB_INCLUDES_PRIVATE 1 +#include "dbus-userdb.h" +#include "dbus-test.h" +#include "dbus-internals.h" +#include "dbus-protocol.h" +#include + +/** + * @addtogroup DBusInternalsUtils + * @{ + */ + +/** + * Checks to see if the UID sent in is the console user + * + * @param uid UID of person to check + * @param error return location for errors + * @returns #TRUE if the UID is the same as the console user and there are no errors + */ +dbus_bool_t +_dbus_is_console_user (dbus_uid_t uid, + DBusError *error) +{ + + DBusUserDatabase *db; + const DBusUserInfo *info; + dbus_bool_t result = FALSE; + +#ifdef HAVE_CONSOLE_OWNER_FILE + + DBusString f; + DBusStat st; + + if (!_dbus_string_init (&f)) + { + _DBUS_SET_OOM (error); + return FALSE; + } + + if (!_dbus_string_append(&f, DBUS_CONSOLE_OWNER_FILE)) + { + _dbus_string_free(&f); + _DBUS_SET_OOM (error); + return FALSE; + } + + if (_dbus_stat(&f, &st, NULL) && (st.uid == uid)) + { + _dbus_string_free(&f); + return TRUE; + } + + _dbus_string_free(&f); + +#endif /* HAVE_CONSOLE_OWNER_FILE */ + + _dbus_user_database_lock_system (); + + db = _dbus_user_database_get_system (); + if (db == NULL) + { + dbus_set_error (error, DBUS_ERROR_FAILED, "Could not get system database."); + _dbus_user_database_unlock_system (); + return FALSE; + } + + /* TPTD: this should be cache-safe, we've locked the DB and + _dbus_user_at_console doesn't pass it on. */ + info = _dbus_user_database_lookup (db, uid, NULL, error); + + if (info == NULL) + { + _dbus_user_database_unlock_system (); + return FALSE; + } + + result = _dbus_user_at_console (info->username, error); + + _dbus_user_database_unlock_system (); + + return result; +} + +/** + * Gets user ID given username + * + * @param username the username + * @param uid return location for UID + * @returns #TRUE if username existed and we got the UID + */ +dbus_bool_t +_dbus_get_user_id (const DBusString *username, + dbus_uid_t *uid) +{ + return _dbus_get_user_id_and_primary_group (username, uid, NULL); +} + +/** + * Gets group ID given groupname + * + * @param groupname the groupname + * @param gid return location for GID + * @returns #TRUE if group name existed and we got the GID + */ +dbus_bool_t +_dbus_get_group_id (const DBusString *groupname, + dbus_gid_t *gid) +{ + DBusUserDatabase *db; + const DBusGroupInfo *info; + _dbus_user_database_lock_system (); + + db = _dbus_user_database_get_system (); + if (db == NULL) + { + _dbus_user_database_unlock_system (); + return FALSE; + } + + if (!_dbus_user_database_get_groupname (db, groupname, + &info, NULL)) + { + _dbus_user_database_unlock_system (); + return FALSE; + } + + *gid = info->gid; + + _dbus_user_database_unlock_system (); + return TRUE; +} + +/** + * Gets user ID and primary group given username + * + * @param username the username + * @param uid_p return location for UID + * @param gid_p return location for GID + * @returns #TRUE if username existed and we got the UID and GID + */ +dbus_bool_t +_dbus_get_user_id_and_primary_group (const DBusString *username, + dbus_uid_t *uid_p, + dbus_gid_t *gid_p) +{ + DBusUserDatabase *db; + const DBusUserInfo *info; + _dbus_user_database_lock_system (); + + db = _dbus_user_database_get_system (); + if (db == NULL) + { + _dbus_user_database_unlock_system (); + return FALSE; + } + + if (!_dbus_user_database_get_username (db, username, + &info, NULL)) + { + _dbus_user_database_unlock_system (); + return FALSE; + } + + if (uid_p) + *uid_p = info->uid; + if (gid_p) + *gid_p = info->primary_gid; + + _dbus_user_database_unlock_system (); + return TRUE; +} + +/** + * Looks up a gid or group name in the user database. Only one of + * name or GID can be provided. There are wrapper functions for this + * that are better to use, this one does no locking or anything on the + * database and otherwise sort of sucks. + * + * @param db the database + * @param gid the group ID or #DBUS_GID_UNSET + * @param groupname group name or #NULL + * @param error error to fill in + * @returns the entry in the database + */ +DBusGroupInfo* +_dbus_user_database_lookup_group (DBusUserDatabase *db, + dbus_gid_t gid, + const DBusString *groupname, + DBusError *error) +{ + DBusGroupInfo *info; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + /* See if the group is really a number */ + if (gid == DBUS_UID_UNSET) + { + unsigned long n; + + if (_dbus_is_a_number (groupname, &n)) + gid = n; + } + +#ifdef DBUS_ENABLE_USERDB_CACHE + if (gid != DBUS_GID_UNSET) + info = _dbus_hash_table_lookup_ulong (db->groups, gid); + else + info = _dbus_hash_table_lookup_string (db->groups_by_name, + _dbus_string_get_const_data (groupname)); + if (info) + { + _dbus_verbose ("Using cache for GID "DBUS_GID_FORMAT" information\n", + info->gid); + return info; + } + else +#else + if (1) +#endif + { + if (gid != DBUS_GID_UNSET) + _dbus_verbose ("No cache for GID "DBUS_GID_FORMAT"\n", + gid); + else + _dbus_verbose ("No cache for groupname \"%s\"\n", + _dbus_string_get_const_data (groupname)); + + info = dbus_new0 (DBusGroupInfo, 1); + if (info == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return NULL; + } + + if (gid != DBUS_GID_UNSET) + { + if (!_dbus_group_info_fill_gid (info, gid, error)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + _dbus_group_info_free_allocated (info); + return NULL; + } + } + else + { + if (!_dbus_group_info_fill (info, groupname, error)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + _dbus_group_info_free_allocated (info); + return NULL; + } + } + + /* don't use these past here */ + gid = DBUS_GID_UNSET; + groupname = NULL; + + if (!_dbus_hash_table_insert_ulong (db->groups, info->gid, info)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + _dbus_group_info_free_allocated (info); + return NULL; + } + + + if (!_dbus_hash_table_insert_string (db->groups_by_name, + info->groupname, + info)) + { + _dbus_hash_table_remove_ulong (db->groups, info->gid); + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return NULL; + } + + return info; + } +} + + +/** + * Gets the user information for the given group name, + * returned group info should not be freed. + * + * @param db user database + * @param groupname the group name + * @param info return location for const ref to group info + * @param error error location + * @returns #FALSE if error is set + */ +dbus_bool_t +_dbus_user_database_get_groupname (DBusUserDatabase *db, + const DBusString *groupname, + const DBusGroupInfo **info, + DBusError *error) +{ + *info = _dbus_user_database_lookup_group (db, DBUS_GID_UNSET, groupname, error); + return *info != NULL; +} + +/** + * Gets the user information for the given GID, + * returned group info should not be freed. + * + * @param db user database + * @param gid the group ID + * @param info return location for const ref to group info + * @param error error location + * @returns #FALSE if error is set + */ +dbus_bool_t +_dbus_user_database_get_gid (DBusUserDatabase *db, + dbus_gid_t gid, + const DBusGroupInfo **info, + DBusError *error) +{ + *info = _dbus_user_database_lookup_group (db, gid, NULL, error); + return *info != NULL; +} + + +/** + * Gets all groups corresponding to the given UID. Returns #FALSE + * if no memory, or user isn't known, but always initializes + * group_ids to a NULL array. + * + * @param uid the UID + * @param group_ids return location for array of group IDs + * @param n_group_ids return location for length of returned array + * @returns #TRUE if the UID existed and we got some credentials + */ +dbus_bool_t +_dbus_groups_from_uid (dbus_uid_t uid, + dbus_gid_t **group_ids, + int *n_group_ids) +{ + DBusUserDatabase *db; + const DBusUserInfo *info; + *group_ids = NULL; + *n_group_ids = 0; + + _dbus_user_database_lock_system (); + + db = _dbus_user_database_get_system (); + if (db == NULL) + { + _dbus_user_database_unlock_system (); + return FALSE; + } + + if (!_dbus_user_database_get_uid (db, uid, + &info, NULL)) + { + _dbus_user_database_unlock_system (); + return FALSE; + } + + _dbus_assert (info->uid == uid); + + if (info->n_group_ids > 0) + { + *group_ids = dbus_new (dbus_gid_t, info->n_group_ids); + if (*group_ids == NULL) + { + _dbus_user_database_unlock_system (); + return FALSE; + } + + *n_group_ids = info->n_group_ids; + + memcpy (*group_ids, info->group_ids, info->n_group_ids * sizeof (dbus_gid_t)); + } + + _dbus_user_database_unlock_system (); + return TRUE; +} +/** @} */ + +#ifdef DBUS_BUILD_TESTS +#include + +/** + * Unit test for dbus-userdb.c. + * + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_userdb_test (const char *test_data_dir) +{ + const DBusString *username; + const DBusString *homedir; + dbus_uid_t uid; + unsigned long *group_ids; + int n_group_ids, i; + + if (!_dbus_username_from_current_process (&username)) + _dbus_assert_not_reached ("didn't get username"); + + if (!_dbus_homedir_from_current_process (&homedir)) + _dbus_assert_not_reached ("didn't get homedir"); + + if (!_dbus_get_user_id (username, &uid)) + _dbus_assert_not_reached ("didn't get uid"); + + if (!_dbus_groups_from_uid (uid, &group_ids, &n_group_ids)) + _dbus_assert_not_reached ("didn't get groups"); + + printf (" Current user: %s homedir: %s gids:", + _dbus_string_get_const_data (username), + _dbus_string_get_const_data (homedir)); + + for (i=0; i + +/** + * @addtogroup DBusInternalsUtils + * @{ + */ + +/** + * Frees the given #DBusUserInfo's members with _dbus_user_info_free() + * and also calls dbus_free() on the block itself + * + * @param info the info + */ +void +_dbus_user_info_free_allocated (DBusUserInfo *info) +{ + if (info == NULL) /* hash table will pass NULL */ + return; + + _dbus_user_info_free (info); + dbus_free (info); +} + +/** + * Frees the given #DBusGroupInfo's members with _dbus_group_info_free() + * and also calls dbus_free() on the block itself + * + * @param info the info + */ +void +_dbus_group_info_free_allocated (DBusGroupInfo *info) +{ + if (info == NULL) /* hash table will pass NULL */ + return; + + _dbus_group_info_free (info); + dbus_free (info); +} + +/** + * Frees the members of info + * (but not info itself) + * @param info the user info struct + */ +void +_dbus_user_info_free (DBusUserInfo *info) +{ + dbus_free (info->group_ids); + dbus_free (info->username); + dbus_free (info->homedir); +} + +/** + * Frees the members of info (but not info itself). + * + * @param info the group info + */ +void +_dbus_group_info_free (DBusGroupInfo *info) +{ + dbus_free (info->groupname); +} + +/** + * Checks if a given string is actually a number + * and converts it if it is + * + * @param str the string to check + * @param num the memory location of the unsigned long to fill in + * @returns TRUE if str is a number and num is filled in + */ +dbus_bool_t +_dbus_is_a_number (const DBusString *str, + unsigned long *num) +{ + int end; + + if (_dbus_string_parse_uint (str, 0, num, &end) && + end == _dbus_string_get_length (str)) + return TRUE; + else + return FALSE; +} + +/** + * Looks up a uid or username in the user database. Only one of name + * or UID can be provided. There are wrapper functions for this that + * are better to use, this one does no locking or anything on the + * database and otherwise sort of sucks. + * + * @param db the database + * @param uid the user ID or #DBUS_UID_UNSET + * @param username username or #NULL + * @param error error to fill in + * @returns the entry in the database + */ +DBusUserInfo* +_dbus_user_database_lookup (DBusUserDatabase *db, + dbus_uid_t uid, + const DBusString *username, + DBusError *error) +{ + DBusUserInfo *info; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + _dbus_assert (uid != DBUS_UID_UNSET || username != NULL); + + /* See if the username is really a number */ + if (uid == DBUS_UID_UNSET) + { + unsigned long n; + + if (_dbus_is_a_number (username, &n)) + uid = n; + } + +#ifdef DBUS_ENABLE_USERDB_CACHE + if (uid != DBUS_UID_UNSET) + info = _dbus_hash_table_lookup_ulong (db->users, uid); + else + info = _dbus_hash_table_lookup_string (db->users_by_name, _dbus_string_get_const_data (username)); + + if (info) + { + _dbus_verbose ("Using cache for UID "DBUS_UID_FORMAT" information\n", + info->uid); + return info; + } + else +#else + if (1) +#endif + { + if (uid != DBUS_UID_UNSET) + _dbus_verbose ("No cache for UID "DBUS_UID_FORMAT"\n", + uid); + else + _dbus_verbose ("No cache for user \"%s\"\n", + _dbus_string_get_const_data (username)); + + info = dbus_new0 (DBusUserInfo, 1); + if (info == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return NULL; + } + + if (uid != DBUS_UID_UNSET) + { + if (!_dbus_user_info_fill_uid (info, uid, error)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + _dbus_user_info_free_allocated (info); + return NULL; + } + } + else + { + if (!_dbus_user_info_fill (info, username, error)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + _dbus_user_info_free_allocated (info); + return NULL; + } + } + + /* be sure we don't use these after here */ + uid = DBUS_UID_UNSET; + username = NULL; + + /* insert into hash */ + if (!_dbus_hash_table_insert_ulong (db->users, info->uid, info)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + _dbus_user_info_free_allocated (info); + return NULL; + } + + if (!_dbus_hash_table_insert_string (db->users_by_name, + info->username, + info)) + { + _dbus_hash_table_remove_ulong (db->users, info->uid); + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return NULL; + } + + return info; + } +} + +static dbus_bool_t database_locked = FALSE; +static DBusUserDatabase *system_db = NULL; +static DBusString process_username; +static DBusString process_homedir; + +static void +shutdown_system_db (void *data) +{ + _dbus_user_database_unref (system_db); + system_db = NULL; + _dbus_string_free (&process_username); + _dbus_string_free (&process_homedir); +} + +static dbus_bool_t +init_system_db (void) +{ + _dbus_assert (database_locked); + + if (system_db == NULL) + { + DBusError error = DBUS_ERROR_INIT; + const DBusUserInfo *info; + + system_db = _dbus_user_database_new (); + if (system_db == NULL) + return FALSE; + + if (!_dbus_user_database_get_uid (system_db, + _dbus_getuid (), + &info, + &error)) + { + _dbus_user_database_unref (system_db); + system_db = NULL; + + if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY)) + { + dbus_error_free (&error); + return FALSE; + } + else + { + /* This really should not happen. */ + _dbus_warn ("Could not get password database information for UID of current process: %s\n", + error.message); + dbus_error_free (&error); + return FALSE; + } + } + + if (!_dbus_string_init (&process_username)) + { + _dbus_user_database_unref (system_db); + system_db = NULL; + return FALSE; + } + + if (!_dbus_string_init (&process_homedir)) + { + _dbus_string_free (&process_username); + _dbus_user_database_unref (system_db); + system_db = NULL; + return FALSE; + } + + if (!_dbus_string_append (&process_username, + info->username) || + !_dbus_string_append (&process_homedir, + info->homedir) || + !_dbus_register_shutdown_func (shutdown_system_db, NULL)) + { + _dbus_string_free (&process_username); + _dbus_string_free (&process_homedir); + _dbus_user_database_unref (system_db); + system_db = NULL; + return FALSE; + } + } + + return TRUE; +} + +/** + * Locks global system user database. + */ +void +_dbus_user_database_lock_system (void) +{ + _DBUS_LOCK (system_users); + database_locked = TRUE; +} + +/** + * Unlocks global system user database. + */ +void +_dbus_user_database_unlock_system (void) +{ + database_locked = FALSE; + _DBUS_UNLOCK (system_users); +} + +/** + * Gets the system global user database; + * must be called with lock held (_dbus_user_database_lock_system()). + * + * @returns the database or #NULL if no memory + */ +DBusUserDatabase* +_dbus_user_database_get_system (void) +{ + _dbus_assert (database_locked); + + init_system_db (); + + return system_db; +} + +/** + * Flushes the system global user database; + */ +void +_dbus_user_database_flush_system (void) +{ + _dbus_user_database_lock_system (); + + _dbus_user_database_flush (system_db); + + _dbus_user_database_unlock_system (); +} + +/** + * Gets username of user owning current process. The returned string + * is valid until dbus_shutdown() is called. + * + * @param username place to store pointer to username + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_username_from_current_process (const DBusString **username) +{ + _dbus_user_database_lock_system (); + if (!init_system_db ()) + { + _dbus_user_database_unlock_system (); + return FALSE; + } + *username = &process_username; + _dbus_user_database_unlock_system (); + + return TRUE; +} + +/** + * Gets homedir of user owning current process. The returned string + * is valid until dbus_shutdown() is called. + * + * @param homedir place to store pointer to homedir + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_homedir_from_current_process (const DBusString **homedir) +{ + _dbus_user_database_lock_system (); + if (!init_system_db ()) + { + _dbus_user_database_unlock_system (); + return FALSE; + } + *homedir = &process_homedir; + _dbus_user_database_unlock_system (); + + return TRUE; +} + +/** + * Gets the home directory for the given user. + * + * @param username the username + * @param homedir string to append home directory to + * @returns #TRUE if user existed and we appended their homedir + */ +dbus_bool_t +_dbus_homedir_from_username (const DBusString *username, + DBusString *homedir) +{ + DBusUserDatabase *db; + const DBusUserInfo *info; + _dbus_user_database_lock_system (); + + db = _dbus_user_database_get_system (); + if (db == NULL) + { + _dbus_user_database_unlock_system (); + return FALSE; + } + + if (!_dbus_user_database_get_username (db, username, + &info, NULL)) + { + _dbus_user_database_unlock_system (); + return FALSE; + } + + if (!_dbus_string_append (homedir, info->homedir)) + { + _dbus_user_database_unlock_system (); + return FALSE; + } + + _dbus_user_database_unlock_system (); + return TRUE; +} + +/** + * Gets the home directory for the given user. + * + * @param uid the uid + * @param homedir string to append home directory to + * @returns #TRUE if user existed and we appended their homedir + */ +dbus_bool_t +_dbus_homedir_from_uid (dbus_uid_t uid, + DBusString *homedir) +{ + DBusUserDatabase *db; + const DBusUserInfo *info; + _dbus_user_database_lock_system (); + + db = _dbus_user_database_get_system (); + if (db == NULL) + { + _dbus_user_database_unlock_system (); + return FALSE; + } + + if (!_dbus_user_database_get_uid (db, uid, + &info, NULL)) + { + _dbus_user_database_unlock_system (); + return FALSE; + } + + if (!_dbus_string_append (homedir, info->homedir)) + { + _dbus_user_database_unlock_system (); + return FALSE; + } + + _dbus_user_database_unlock_system (); + return TRUE; +} + +/** + * Adds the credentials corresponding to the given username. + * + * Used among other purposes to parses a desired identity provided + * from a client in the auth protocol. On UNIX this means parsing a + * UID, on Windows probably parsing an SID string. + * + * @todo this is broken because it treats OOM and parse error + * the same way. Needs a #DBusError. + * + * @param credentials credentials to fill in + * @param username the username + * @returns #TRUE if the username existed and we got some credentials + */ +dbus_bool_t +_dbus_credentials_add_from_user (DBusCredentials *credentials, + const DBusString *username) +{ + DBusUserDatabase *db; + const DBusUserInfo *info; + + _dbus_user_database_lock_system (); + + db = _dbus_user_database_get_system (); + if (db == NULL) + { + _dbus_user_database_unlock_system (); + return FALSE; + } + + if (!_dbus_user_database_get_username (db, username, + &info, NULL)) + { + _dbus_user_database_unlock_system (); + return FALSE; + } + + if (!_dbus_credentials_add_unix_uid(credentials, info->uid)) + { + _dbus_user_database_unlock_system (); + return FALSE; + } + + _dbus_user_database_unlock_system (); + return TRUE; +} + +/** + * Creates a new user database object used to look up and + * cache user information. + * @returns new database, or #NULL on out of memory + */ +DBusUserDatabase* +_dbus_user_database_new (void) +{ + DBusUserDatabase *db; + + db = dbus_new0 (DBusUserDatabase, 1); + if (db == NULL) + return NULL; + + db->refcount = 1; + + db->users = _dbus_hash_table_new (DBUS_HASH_ULONG, + NULL, (DBusFreeFunction) _dbus_user_info_free_allocated); + + if (db->users == NULL) + goto failed; + + db->groups = _dbus_hash_table_new (DBUS_HASH_ULONG, + NULL, (DBusFreeFunction) _dbus_group_info_free_allocated); + + if (db->groups == NULL) + goto failed; + + db->users_by_name = _dbus_hash_table_new (DBUS_HASH_STRING, + NULL, NULL); + if (db->users_by_name == NULL) + goto failed; + + db->groups_by_name = _dbus_hash_table_new (DBUS_HASH_STRING, + NULL, NULL); + if (db->groups_by_name == NULL) + goto failed; + + return db; + + failed: + _dbus_user_database_unref (db); + return NULL; +} + +/** + * Flush all information out of the user database. + */ +void +_dbus_user_database_flush (DBusUserDatabase *db) +{ + _dbus_hash_table_remove_all(db->users_by_name); + _dbus_hash_table_remove_all(db->groups_by_name); + _dbus_hash_table_remove_all(db->users); + _dbus_hash_table_remove_all(db->groups); +} + +#ifdef DBUS_BUILD_TESTS +/** + * Increments refcount of user database. + * @param db the database + * @returns the database + */ +DBusUserDatabase * +_dbus_user_database_ref (DBusUserDatabase *db) +{ + _dbus_assert (db->refcount > 0); + + db->refcount += 1; + + return db; +} +#endif /* DBUS_BUILD_TESTS */ + +/** + * Decrements refcount of user database. + * @param db the database + */ +void +_dbus_user_database_unref (DBusUserDatabase *db) +{ + _dbus_assert (db->refcount > 0); + + db->refcount -= 1; + if (db->refcount == 0) + { + if (db->users) + _dbus_hash_table_unref (db->users); + + if (db->groups) + _dbus_hash_table_unref (db->groups); + + if (db->users_by_name) + _dbus_hash_table_unref (db->users_by_name); + + if (db->groups_by_name) + _dbus_hash_table_unref (db->groups_by_name); + + dbus_free (db); + } +} + +/** + * Gets the user information for the given UID, + * returned user info should not be freed. + * + * @param db user database + * @param uid the user ID + * @param info return location for const ref to user info + * @param error error location + * @returns #FALSE if error is set + */ +dbus_bool_t +_dbus_user_database_get_uid (DBusUserDatabase *db, + dbus_uid_t uid, + const DBusUserInfo **info, + DBusError *error) +{ + *info = _dbus_user_database_lookup (db, uid, NULL, error); + return *info != NULL; +} + +/** + * Gets the user information for the given username. + * + * @param db user database + * @param username the user name + * @param info return location for const ref to user info + * @param error error location + * @returns #FALSE if error is set + */ +dbus_bool_t +_dbus_user_database_get_username (DBusUserDatabase *db, + const DBusString *username, + const DBusUserInfo **info, + DBusError *error) +{ + *info = _dbus_user_database_lookup (db, DBUS_UID_UNSET, username, error); + return *info != NULL; +} + +/** @} */ + +/* Tests in dbus-userdb-util.c */ diff --git a/src/dbus/dbus-userdb.h b/src/dbus/dbus-userdb.h new file mode 100644 index 0000000..a1153ee --- /dev/null +++ b/src/dbus/dbus-userdb.h @@ -0,0 +1,121 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-userdb.h User database abstraction + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef DBUS_USERDB_H +#define DBUS_USERDB_H + +#include + +#ifdef DBUS_WIN +#error "Don't include this on Windows" +#endif + +DBUS_BEGIN_DECLS + +typedef struct DBusUserDatabase DBusUserDatabase; + +#ifdef DBUS_USERDB_INCLUDES_PRIVATE +#include + +/** + * Internals of DBusUserDatabase + */ +struct DBusUserDatabase +{ + int refcount; /**< Reference count */ + + DBusHashTable *users; /**< Users in the database by UID */ + DBusHashTable *groups; /**< Groups in the database by GID */ + DBusHashTable *users_by_name; /**< Users in the database by name */ + DBusHashTable *groups_by_name; /**< Groups in the database by name */ + +}; + + +DBusUserDatabase* _dbus_user_database_new (void); +DBusUserDatabase* _dbus_user_database_ref (DBusUserDatabase *db); +void _dbus_user_database_flush (DBusUserDatabase *db); +void _dbus_user_database_unref (DBusUserDatabase *db); +dbus_bool_t _dbus_user_database_get_uid (DBusUserDatabase *db, + dbus_uid_t uid, + const DBusUserInfo **info, + DBusError *error); +dbus_bool_t _dbus_user_database_get_gid (DBusUserDatabase *db, + dbus_gid_t gid, + const DBusGroupInfo **info, + DBusError *error); +dbus_bool_t _dbus_user_database_get_username (DBusUserDatabase *db, + const DBusString *username, + const DBusUserInfo **info, + DBusError *error); +dbus_bool_t _dbus_user_database_get_groupname (DBusUserDatabase *db, + const DBusString *groupname, + const DBusGroupInfo **info, + DBusError *error); + +DBusUserInfo* _dbus_user_database_lookup (DBusUserDatabase *db, + dbus_uid_t uid, + const DBusString *username, + DBusError *error); +DBusGroupInfo* _dbus_user_database_lookup_group (DBusUserDatabase *db, + dbus_gid_t gid, + const DBusString *groupname, + DBusError *error); +void _dbus_user_info_free_allocated (DBusUserInfo *info); +void _dbus_group_info_free_allocated (DBusGroupInfo *info); +#endif /* DBUS_USERDB_INCLUDES_PRIVATE */ + +DBusUserDatabase* _dbus_user_database_get_system (void); +void _dbus_user_database_lock_system (void); +void _dbus_user_database_unlock_system (void); +void _dbus_user_database_flush_system (void); + +dbus_bool_t _dbus_get_user_id (const DBusString *username, + dbus_uid_t *uid); +dbus_bool_t _dbus_get_group_id (const DBusString *group_name, + dbus_gid_t *gid); +dbus_bool_t _dbus_get_user_id_and_primary_group (const DBusString *username, + dbus_uid_t *uid_p, + dbus_gid_t *gid_p); +dbus_bool_t _dbus_credentials_from_uid (dbus_uid_t user_id, + DBusCredentials *credentials); +dbus_bool_t _dbus_groups_from_uid (dbus_uid_t uid, + dbus_gid_t **group_ids, + int *n_group_ids); +dbus_bool_t _dbus_is_console_user (dbus_uid_t uid, + DBusError *error); + +dbus_bool_t _dbus_is_a_number (const DBusString *str, + unsigned long *num); + +dbus_bool_t _dbus_username_from_current_process (const DBusString **username); +dbus_bool_t _dbus_homedir_from_current_process (const DBusString **homedir); +dbus_bool_t _dbus_homedir_from_username (const DBusString *username, + DBusString *homedir); + +dbus_bool_t _dbus_homedir_from_uid (dbus_uid_t uid, + DBusString *homedir); + +DBUS_END_DECLS + +#endif /* DBUS_USERDB_H */ diff --git a/src/dbus/dbus-uuidgen.c b/src/dbus/dbus-uuidgen.c new file mode 100644 index 0000000..6f226bc --- /dev/null +++ b/src/dbus/dbus-uuidgen.c @@ -0,0 +1,129 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-uuidgen.c The guts of the dbus-uuidgen binary live in libdbus, in this file. + * + * Copyright (C) 2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include "dbus-uuidgen.h" +#include "dbus-internals.h" +#include "dbus-string.h" +#include "dbus-protocol.h" + +#ifdef DBUS_WIN +#error "dbus-uuidgen should not be needed on Windows" +#endif + +/** + * @defgroup DBusInternalsUuidgen dbus-uuidgen implementation + * @ingroup DBusInternals + * @brief Functions for dbus-uuidgen binary + * + * These are not considered part of the ABI, and if you call them + * you will get screwed by future changes. + * + * @{ + */ + +static dbus_bool_t +return_uuid (DBusGUID *uuid, + char **uuid_p, + DBusError *error) +{ + if (uuid_p) + { + DBusString encoded; + + if (!_dbus_string_init (&encoded)) + { + _DBUS_SET_OOM (error); + return FALSE; + } + + if (!_dbus_uuid_encode (uuid, &encoded) || + !_dbus_string_steal_data (&encoded, uuid_p)) + { + _DBUS_SET_OOM (error); + _dbus_string_free (&encoded); + return FALSE; + } + _dbus_string_free (&encoded); + } + return TRUE; +} + +/** + * For use by the dbus-uuidgen binary ONLY, do not call this. + * We can and will change this function without modifying + * the libdbus soname. + * + * @param filename the file or #NULL for the machine ID file + * @param uuid_p out param to return the uuid + * @param create_if_not_found whether to create it if not already there + * @param error error return + * @returns #FALSE if error is set + */ +dbus_bool_t +dbus_internal_do_not_use_get_uuid (const char *filename, + char **uuid_p, + dbus_bool_t create_if_not_found, + DBusError *error) +{ + DBusGUID uuid; + + if (filename) + { + DBusString filename_str; + _dbus_string_init_const (&filename_str, filename); + if (!_dbus_read_uuid_file (&filename_str, &uuid, create_if_not_found, error)) + goto error; + } + else + { + if (!_dbus_read_local_machine_uuid (&uuid, create_if_not_found, error)) + goto error; + } + + if (!return_uuid(&uuid, uuid_p, error)) + goto error; + + return TRUE; + + error: + _DBUS_ASSERT_ERROR_IS_SET (error); + return FALSE; +} + +/** + * For use by the dbus-uuidgen binary ONLY, do not call this. + * We can and will change this function without modifying + * the libdbus soname. + * + * @param uuid_p out param to return the uuid + * @returns #FALSE if no memory + */ +dbus_bool_t +dbus_internal_do_not_use_create_uuid (char **uuid_p) +{ + DBusGUID uuid; + + _dbus_generate_uuid (&uuid); + return return_uuid (&uuid, uuid_p, NULL); +} + +/** @} */ diff --git a/src/dbus/dbus-uuidgen.h b/src/dbus/dbus-uuidgen.h new file mode 100644 index 0000000..3e30b99 --- /dev/null +++ b/src/dbus/dbus-uuidgen.h @@ -0,0 +1,47 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-uuidgen.h The guts of the dbus-uuidgen binary live in libdbus, in this file. + * + * Copyright (C) 2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifdef DBUS_INSIDE_DBUS_H +#error "You can't include dbus-uuidgen.h in the public header dbus.h" +#endif + +#ifndef DBUS_UUIDGEN_H +#define DBUS_UUIDGEN_H + +#include +#include + +DBUS_BEGIN_DECLS + +dbus_bool_t dbus_internal_do_not_use_get_uuid (const char *filename, + char **uuid_p, + dbus_bool_t create_if_not_found, + DBusError *error); +dbus_bool_t dbus_internal_do_not_use_ensure_uuid (const char *filename, + char **uuid_p, + DBusError *error); +dbus_bool_t dbus_internal_do_not_use_create_uuid (char **uuid_p); + + +DBUS_END_DECLS + +#endif /* DBUS_UUIDGEN_H */ diff --git a/src/dbus/dbus-watch.c b/src/dbus/dbus-watch.c new file mode 100644 index 0000000..9d6ab7c --- /dev/null +++ b/src/dbus/dbus-watch.c @@ -0,0 +1,668 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-watch.c DBusWatch implementation + * + * Copyright (C) 2002, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-internals.h" +#include "dbus-watch.h" +#include "dbus-list.h" + +/** + * @defgroup DBusWatchInternals DBusWatch implementation details + * @ingroup DBusInternals + * @brief implementation details for DBusWatch + * + * @{ + */ + +/** + * Implementation of DBusWatch + */ +struct DBusWatch +{ + int refcount; /**< Reference count */ + int fd; /**< File descriptor. */ + unsigned int flags; /**< Conditions to watch. */ + + DBusWatchHandler handler; /**< Watch handler. */ + void *handler_data; /**< Watch handler data. */ + DBusFreeFunction free_handler_data_function; /**< Free the watch handler data. */ + + void *data; /**< Application data. */ + DBusFreeFunction free_data_function; /**< Free the application data. */ + unsigned int enabled : 1; /**< Whether it's enabled. */ +}; + +/** + * Creates a new DBusWatch. Used to add a file descriptor to be polled + * by a main loop. + * + * @param fd the file descriptor to be watched. + * @param flags the conditions to watch for on the descriptor. + * @param enabled the initial enabled state + * @param handler the handler function + * @param data data for handler function + * @param free_data_function function to free the data + * @returns the new DBusWatch object. + */ +DBusWatch* +_dbus_watch_new (int fd, + unsigned int flags, + dbus_bool_t enabled, + DBusWatchHandler handler, + void *data, + DBusFreeFunction free_data_function) +{ + DBusWatch *watch; + +#define VALID_WATCH_FLAGS (DBUS_WATCH_WRITABLE | DBUS_WATCH_READABLE) + + _dbus_assert ((flags & VALID_WATCH_FLAGS) == flags); + + watch = dbus_new0 (DBusWatch, 1); + if (watch == NULL) + return NULL; + + watch->refcount = 1; + watch->fd = fd; + watch->flags = flags; + watch->enabled = enabled; + + watch->handler = handler; + watch->handler_data = data; + watch->free_handler_data_function = free_data_function; + + return watch; +} + +/** + * Increments the reference count of a DBusWatch object. + * + * @param watch the watch object. + * @returns the watch object. + */ +DBusWatch * +_dbus_watch_ref (DBusWatch *watch) +{ + watch->refcount += 1; + + return watch; +} + +/** + * Decrements the reference count of a DBusWatch object + * and finalizes the object if the count reaches zero. + * + * @param watch the watch object. + */ +void +_dbus_watch_unref (DBusWatch *watch) +{ + _dbus_assert (watch != NULL); + _dbus_assert (watch->refcount > 0); + + watch->refcount -= 1; + if (watch->refcount == 0) + { + dbus_watch_set_data (watch, NULL, NULL); /* call free_data_function */ + + if (watch->free_handler_data_function) + (* watch->free_handler_data_function) (watch->handler_data); + + dbus_free (watch); + } +} + +/** + * Clears the file descriptor from a now-invalid watch object so that + * no one tries to use it. This is because a watch may stay alive due + * to reference counts after the file descriptor is closed. + * Invalidation makes it easier to catch bugs. It also + * keeps people from doing dorky things like assuming file descriptors + * are unique (never recycled). + * + * @param watch the watch object. + */ +void +_dbus_watch_invalidate (DBusWatch *watch) +{ + watch->fd = -1; + watch->flags = 0; +} + +/** + * Sanitizes the given condition so that it only contains + * flags that the DBusWatch requested. e.g. if the + * watch is a DBUS_WATCH_READABLE watch then + * DBUS_WATCH_WRITABLE will be stripped from the condition. + * + * @param watch the watch object. + * @param condition address of the condition to sanitize. + */ +void +_dbus_watch_sanitize_condition (DBusWatch *watch, + unsigned int *condition) +{ + if (!(watch->flags & DBUS_WATCH_READABLE)) + *condition &= ~DBUS_WATCH_READABLE; + if (!(watch->flags & DBUS_WATCH_WRITABLE)) + *condition &= ~DBUS_WATCH_WRITABLE; +} + + +/** + * @typedef DBusWatchList + * + * Opaque data type representing a list of watches + * and a set of DBusAddWatchFunction/DBusRemoveWatchFunction. + * Automatically handles removing/re-adding watches + * when the DBusAddWatchFunction is updated or changed. + * Holds a reference count to each watch. + * + * Used in the implementation of both DBusServer and + * DBusClient. + * + */ + +/** + * DBusWatchList implementation details. All fields + * are private. + * + */ +struct DBusWatchList +{ + DBusList *watches; /**< Watch objects. */ + + DBusAddWatchFunction add_watch_function; /**< Callback for adding a watch. */ + DBusRemoveWatchFunction remove_watch_function; /**< Callback for removing a watch. */ + DBusWatchToggledFunction watch_toggled_function; /**< Callback on toggling enablement */ + void *watch_data; /**< Data for watch callbacks */ + DBusFreeFunction watch_free_data_function; /**< Free function for watch callback data */ +}; + +/** + * Creates a new watch list. Returns #NULL if insufficient + * memory exists. + * + * @returns the new watch list, or #NULL on failure. + */ +DBusWatchList* +_dbus_watch_list_new (void) +{ + DBusWatchList *watch_list; + + watch_list = dbus_new0 (DBusWatchList, 1); + if (watch_list == NULL) + return NULL; + + return watch_list; +} + +/** + * Frees a DBusWatchList. + * + * @param watch_list the watch list. + */ +void +_dbus_watch_list_free (DBusWatchList *watch_list) +{ + /* free watch_data and removes watches as a side effect */ + _dbus_watch_list_set_functions (watch_list, + NULL, NULL, NULL, NULL, NULL); + _dbus_list_foreach (&watch_list->watches, + (DBusForeachFunction) _dbus_watch_unref, + NULL); + _dbus_list_clear (&watch_list->watches); + + dbus_free (watch_list); +} + +/** + * Sets the watch functions. This function is the "backend" + * for dbus_connection_set_watch_functions() and + * dbus_server_set_watch_functions(). + * + * @param watch_list the watch list. + * @param add_function the add watch function. + * @param remove_function the remove watch function. + * @param toggled_function function on toggling enabled flag, or #NULL + * @param data the data for those functions. + * @param free_data_function the function to free the data. + * @returns #FALSE if not enough memory + * + */ +dbus_bool_t +_dbus_watch_list_set_functions (DBusWatchList *watch_list, + DBusAddWatchFunction add_function, + DBusRemoveWatchFunction remove_function, + DBusWatchToggledFunction toggled_function, + void *data, + DBusFreeFunction free_data_function) +{ + /* Add watches with the new watch function, failing on OOM */ + if (add_function != NULL) + { + DBusList *link; + + link = _dbus_list_get_first_link (&watch_list->watches); + while (link != NULL) + { + DBusList *next = _dbus_list_get_next_link (&watch_list->watches, + link); + +#ifdef DBUS_ENABLE_VERBOSE_MODE + { + const char *watch_type; + int flags; + + flags = dbus_watch_get_flags (link->data); + if ((flags & DBUS_WATCH_READABLE) && + (flags & DBUS_WATCH_WRITABLE)) + watch_type = "readwrite"; + else if (flags & DBUS_WATCH_READABLE) + watch_type = "read"; + else if (flags & DBUS_WATCH_WRITABLE) + watch_type = "write"; + else + watch_type = "not read or write"; + + _dbus_verbose ("Adding a %s watch on fd %d using newly-set add watch function\n", + watch_type, + dbus_watch_get_socket (link->data)); + } +#endif /* DBUS_ENABLE_VERBOSE_MODE */ + + if (!(* add_function) (link->data, data)) + { + /* remove it all again and return FALSE */ + DBusList *link2; + + link2 = _dbus_list_get_first_link (&watch_list->watches); + while (link2 != link) + { + DBusList *next = _dbus_list_get_next_link (&watch_list->watches, + link2); + + _dbus_verbose ("Removing watch on fd %d using newly-set remove function because initial add failed\n", + dbus_watch_get_socket (link2->data)); + + (* remove_function) (link2->data, data); + + link2 = next; + } + + return FALSE; + } + + link = next; + } + } + + /* Remove all current watches from previous watch handlers */ + + if (watch_list->remove_watch_function != NULL) + { + _dbus_verbose ("Removing all pre-existing watches\n"); + + _dbus_list_foreach (&watch_list->watches, + (DBusForeachFunction) watch_list->remove_watch_function, + watch_list->watch_data); + } + + if (watch_list->watch_free_data_function != NULL) + (* watch_list->watch_free_data_function) (watch_list->watch_data); + + watch_list->add_watch_function = add_function; + watch_list->remove_watch_function = remove_function; + watch_list->watch_toggled_function = toggled_function; + watch_list->watch_data = data; + watch_list->watch_free_data_function = free_data_function; + + return TRUE; +} + +/** + * Adds a new watch to the watch list, invoking the + * application DBusAddWatchFunction if appropriate. + * + * @param watch_list the watch list. + * @param watch the watch to add. + * @returns #TRUE on success, #FALSE if no memory. + */ +dbus_bool_t +_dbus_watch_list_add_watch (DBusWatchList *watch_list, + DBusWatch *watch) +{ + if (!_dbus_list_append (&watch_list->watches, watch)) + return FALSE; + + _dbus_watch_ref (watch); + + if (watch_list->add_watch_function != NULL) + { + _dbus_verbose ("Adding watch on fd %d\n", + dbus_watch_get_socket (watch)); + + if (!(* watch_list->add_watch_function) (watch, + watch_list->watch_data)) + { + _dbus_list_remove_last (&watch_list->watches, watch); + _dbus_watch_unref (watch); + return FALSE; + } + } + + return TRUE; +} + +/** + * Removes a watch from the watch list, invoking the + * application's DBusRemoveWatchFunction if appropriate. + * + * @param watch_list the watch list. + * @param watch the watch to remove. + */ +void +_dbus_watch_list_remove_watch (DBusWatchList *watch_list, + DBusWatch *watch) +{ + if (!_dbus_list_remove (&watch_list->watches, watch)) + _dbus_assert_not_reached ("Nonexistent watch was removed"); + + if (watch_list->remove_watch_function != NULL) + { + _dbus_verbose ("Removing watch on fd %d\n", + dbus_watch_get_socket (watch)); + + (* watch_list->remove_watch_function) (watch, + watch_list->watch_data); + } + + _dbus_watch_unref (watch); +} + +/** + * Sets a watch to the given enabled state, invoking the + * application's DBusWatchToggledFunction if appropriate. + * + * @param watch_list the watch list. + * @param watch the watch to toggle. + * @param enabled #TRUE to enable + */ +void +_dbus_watch_list_toggle_watch (DBusWatchList *watch_list, + DBusWatch *watch, + dbus_bool_t enabled) +{ + enabled = !!enabled; + + if (enabled == watch->enabled) + return; + + watch->enabled = enabled; + + if (watch_list->watch_toggled_function != NULL) + { + _dbus_verbose ("Toggling watch %p on fd %d to %d\n", + watch, dbus_watch_get_socket (watch), watch->enabled); + + (* watch_list->watch_toggled_function) (watch, + watch_list->watch_data); + } +} + +/** + * Sets the handler for the watch. + * + * @todo this function only exists because of the weird + * way connection watches are done, see the note + * in docs for _dbus_connection_handle_watch(). + * + * @param watch the watch + * @param handler the new handler + * @param data the data + * @param free_data_function free data with this + */ +void +_dbus_watch_set_handler (DBusWatch *watch, + DBusWatchHandler handler, + void *data, + DBusFreeFunction free_data_function) +{ + if (watch->free_handler_data_function) + (* watch->free_handler_data_function) (watch->handler_data); + + watch->handler = handler; + watch->handler_data = data; + watch->free_handler_data_function = free_data_function; +} + +/** @} */ + +/** + * @defgroup DBusWatch DBusWatch + * @ingroup DBus + * @brief Object representing a file descriptor to be watched. + * + * Types and functions related to DBusWatch. A watch represents + * a file descriptor that the main loop needs to monitor, + * as in Qt's QSocketNotifier or GLib's g_io_add_watch(). + * + * Use dbus_connection_set_watch_functions() or dbus_server_set_watch_functions() + * to be notified when libdbus needs to add or remove watches. + * + * @{ + */ + +/** + * @typedef DBusWatch + * + * Opaque object representing a file descriptor + * to be watched for changes in readability, + * writability, or hangup. + */ + +/** + * Deprecated former name of dbus_watch_get_unix_fd(). + * + * @param watch the DBusWatch object. + * @returns the file descriptor to watch. + */ +int +dbus_watch_get_fd (DBusWatch *watch) +{ + return dbus_watch_get_unix_fd(watch); +} + +/** + * Returns a UNIX file descriptor to be watched, + * which may be a pipe, socket, or other type of + * descriptor. On UNIX this is preferred to + * dbus_watch_get_socket() since it works with + * more kinds of #DBusWatch. + * + * Always returns -1 on Windows. On Windows you use + * dbus_watch_get_socket() to get a Winsock socket to watch. + * + * @param watch the DBusWatch object. + * @returns the file descriptor to watch. + */ +int +dbus_watch_get_unix_fd (DBusWatch *watch) +{ + /* FIXME remove #ifdef and do this on a lower level + * (watch should have set_socket and set_unix_fd and track + * which it has, and the transport should provide the + * appropriate watch type) + */ +#ifdef DBUS_UNIX + return watch->fd; +#else + return -1; +#endif +} + +/** + * Returns a socket to be watched, on UNIX this will return -1 if our + * transport is not socket-based so dbus_watch_get_unix_fd() is + * preferred. + * + * On Windows, dbus_watch_get_unix_fd() returns -1 but this function + * returns a Winsock socket (assuming the transport is socket-based, + * as it always is for now). + * + * @param watch the DBusWatch object. + * @returns the socket to watch. + */ +int +dbus_watch_get_socket (DBusWatch *watch) +{ + return watch->fd; +} + +/** + * Gets flags from DBusWatchFlags indicating + * what conditions should be monitored on the + * file descriptor. + * + * The flags returned will only contain DBUS_WATCH_READABLE + * and DBUS_WATCH_WRITABLE, never DBUS_WATCH_HANGUP or + * DBUS_WATCH_ERROR; all watches implicitly include a watch + * for hangups, errors, and other exceptional conditions. + * + * @param watch the DBusWatch object. + * @returns the conditions to watch. + */ +unsigned int +dbus_watch_get_flags (DBusWatch *watch) +{ + _dbus_assert ((watch->flags & VALID_WATCH_FLAGS) == watch->flags); + + return watch->flags; +} + +/** + * Gets data previously set with dbus_watch_set_data() + * or #NULL if none. + * + * @param watch the DBusWatch object. + * @returns previously-set data. + */ +void* +dbus_watch_get_data (DBusWatch *watch) +{ + return watch->data; +} + +/** + * Sets data which can be retrieved with dbus_watch_get_data(). + * Intended for use by the DBusAddWatchFunction and + * DBusRemoveWatchFunction to store their own data. For example with + * Qt you might store the QSocketNotifier for this watch and with GLib + * you might store a GSource. + * + * @param watch the DBusWatch object. + * @param data the data. + * @param free_data_function function to be called to free the data. + */ +void +dbus_watch_set_data (DBusWatch *watch, + void *data, + DBusFreeFunction free_data_function) +{ + _dbus_verbose ("Setting watch fd %d data to data = %p function = %p from data = %p function = %p\n", + dbus_watch_get_socket (watch), + data, free_data_function, watch->data, watch->free_data_function); + + if (watch->free_data_function != NULL) + (* watch->free_data_function) (watch->data); + + watch->data = data; + watch->free_data_function = free_data_function; +} + +/** + * Returns whether a watch is enabled or not. If not + * enabled, it should not be polled by the main loop. + * + * @param watch the DBusWatch object + * @returns #TRUE if the watch is enabled + */ +dbus_bool_t +dbus_watch_get_enabled (DBusWatch *watch) +{ + _dbus_assert (watch != NULL); + return watch->enabled; +} + + +/** + * Called to notify the D-Bus library when a previously-added watch is + * ready for reading or writing, or has an exception such as a hangup. + * + * If this function returns #FALSE, then the file descriptor may still + * be ready for reading or writing, but more memory is needed in order + * to do the reading or writing. If you ignore the #FALSE return, your + * application may spin in a busy loop on the file descriptor until + * memory becomes available, but nothing more catastrophic should + * happen. + * + * dbus_watch_handle() cannot be called during the + * DBusAddWatchFunction, as the connection will not be ready to handle + * that watch yet. + * + * It is not allowed to reference a DBusWatch after it has been passed + * to remove_function. + * + * @param watch the DBusWatch object. + * @param flags the poll condition using #DBusWatchFlags values + * @returns #FALSE if there wasn't enough memory + */ +dbus_bool_t +dbus_watch_handle (DBusWatch *watch, + unsigned int flags) +{ +#ifndef DBUS_DISABLE_CHECKS + if (watch->fd < 0 || watch->flags == 0) + { + _dbus_warn_check_failed ("%s: Watch is invalid, it should have been removed\n", + _DBUS_FUNCTION_NAME); + return TRUE; + } +#endif + + _dbus_return_val_if_fail (watch->fd >= 0 /* fails if watch was removed */, TRUE); + + _dbus_watch_sanitize_condition (watch, &flags); + + if (flags == 0) + { + _dbus_verbose ("After sanitization, watch flags on fd %d were 0\n", + watch->fd); + return TRUE; + } + else + return (* watch->handler) (watch, flags, + watch->handler_data); +} + + +/** @} */ diff --git a/src/dbus/dbus-watch.h b/src/dbus/dbus-watch.h new file mode 100644 index 0000000..1d8d327 --- /dev/null +++ b/src/dbus/dbus-watch.h @@ -0,0 +1,82 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-watch.h DBusWatch internal interfaces + * + * Copyright (C) 2002 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef DBUS_WATCH_H +#define DBUS_WATCH_H + +#include +#include + +DBUS_BEGIN_DECLS + +/** + * @addtogroup DBusWatchInternals + * @{ + */ + +/* Public methods on DBusWatch are in dbus-connection.h */ + +typedef struct DBusWatchList DBusWatchList; + +/** function to run when the watch is handled */ +typedef dbus_bool_t (* DBusWatchHandler) (DBusWatch *watch, + unsigned int flags, + void *data); + +DBusWatch* _dbus_watch_new (int fd, + unsigned int flags, + dbus_bool_t enabled, + DBusWatchHandler handler, + void *data, + DBusFreeFunction free_data_function); +DBusWatch* _dbus_watch_ref (DBusWatch *watch); +void _dbus_watch_unref (DBusWatch *watch); +void _dbus_watch_invalidate (DBusWatch *watch); +void _dbus_watch_sanitize_condition (DBusWatch *watch, + unsigned int *condition); +void _dbus_watch_set_handler (DBusWatch *watch, + DBusWatchHandler handler, + void *data, + DBusFreeFunction free_data_function); + + +DBusWatchList* _dbus_watch_list_new (void); +void _dbus_watch_list_free (DBusWatchList *watch_list); +dbus_bool_t _dbus_watch_list_set_functions (DBusWatchList *watch_list, + DBusAddWatchFunction add_function, + DBusRemoveWatchFunction remove_function, + DBusWatchToggledFunction toggled_function, + void *data, + DBusFreeFunction free_data_function); +dbus_bool_t _dbus_watch_list_add_watch (DBusWatchList *watch_list, + DBusWatch *watch); +void _dbus_watch_list_remove_watch (DBusWatchList *watch_list, + DBusWatch *watch); +void _dbus_watch_list_toggle_watch (DBusWatchList *watch_list, + DBusWatch *watch, + dbus_bool_t enabled); + +/** @} */ + +DBUS_END_DECLS + +#endif /* DBUS_WATCH_H */ diff --git a/src/dbus/dbus.h b/src/dbus/dbus.h new file mode 100644 index 0000000..880f21d --- /dev/null +++ b/src/dbus/dbus.h @@ -0,0 +1,103 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus.h Convenience header including all other headers + * + * Copyright (C) 2002, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef DBUS_H +#define DBUS_H + +#define DBUS_INSIDE_DBUS_H 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DBUS_INSIDE_DBUS_H + +/** + * @defgroup DBus D-Bus low-level public API + * @brief The low-level public API of the D-Bus library + * + * libdbus provides a low-level C API intended primarily for use by + * bindings to specific object systems and languages. D-Bus is most + * convenient when used with the GLib bindings, Python bindings, Qt + * bindings, Mono bindings, and so forth. This low-level API has a + * lot of complexity useful only for bindings. + * + * @{ + */ + +/** @} */ + +/** + * @mainpage + * + * This manual documents the low-level D-Bus C API. If you use + * this low-level API directly, you're signing up for some pain. + * + * Caveats aside, you might get started learning the low-level API by reading + * about @ref DBusConnection and @ref DBusMessage. + * + * There are several other places to look for D-Bus information, such + * as the tutorial and the specification; those can be found at the D-Bus + * website. If you're interested in a sysadmin or package + * maintainer's perspective on the dbus-daemon itself and its + * configuration, be sure to check out the man pages as well. + * + * The low-level API documented in this manual deliberately lacks + * most convenience functions - those are left up to higher-level libraries + * based on frameworks such as GLib, Qt, Python, Mono, Java, + * etc. These higher-level libraries (often called "D-Bus bindings") + * have features such as object systems and main loops that allow a + * much more convenient API. + * + * The low-level API also contains plenty of clutter to support + * integration with arbitrary object systems, languages, main loops, + * and so forth. These features add a lot of noise to the API that you + * probably don't care about unless you're coding a binding. + * + * This manual also contains docs for @ref DBusInternals "D-Bus internals", + * so you can use it to get oriented to the D-Bus source code if you're + * interested in patching the code. You should also read the + * file HACKING which comes with the source code if you plan to contribute to + * D-Bus. + * + * As you read the code, you can identify internal D-Bus functions + * because they start with an underscore ('_') character. Also, any + * identifier or macro that lacks a DBus, dbus_, or DBUS_ namepace + * prefix is internal, with a couple of exceptions such as #NULL, + * #TRUE, and #FALSE. + */ + +#endif /* DBUS_H */ diff --git a/src/defconfig.h b/src/defconfig.h deleted file mode 100644 index 3767fe9..0000000 --- a/src/defconfig.h +++ /dev/null @@ -1,90 +0,0 @@ -#ifndef __DEFCONFIG_H -#define __DEFCONFIG_H - -#define defconfig { \ -"# Conky, a system monitor, based on torsmo\n", \ -"#\n", \ -"# Any original torsmo code is licensed under the BSD license\n", \ -"#\n", \ -"# All code written since the fork of torsmo is licensed under the GPL\n", \ -"#\n", \ -"# Please see COPYING for details\n", \ -"#\n", \ -"# Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen\n", \ -"# Copyright (c) 2005-2010 Brenden Matthews, Philip Kovacs, et. al. (see AUTHORS)\n", \ -"# All rights reserved.\n", \ -"#\n", \ -"# This program is free software: you can redistribute it and/or modify\n", \ -"# it under the terms of the GNU General Public License as published by\n", \ -"# the Free Software Foundation, either version 3 of the License, or\n", \ -"# (at your option) any later version.\n", \ -"#\n", \ -"# This program is distributed in the hope that it will be useful,\n", \ -"# but WITHOUT ANY WARRANTY; without even the implied warranty of\n", \ -"# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n", \ -"# GNU General Public License for more details.\n", \ -"# You should have received a copy of the GNU General Public License\n", \ -"# along with this program. If not, see .\n", \ -"#\n", \ -"\n", \ -"alignment top_left\n", \ -"background no\n", \ -"border_width 1\n", \ -"cpu_avg_samples 2\n", \ -"default_color white\n", \ -"default_outline_color white\n", \ -"default_shade_color white\n", \ -"draw_borders no\n", \ -"draw_graph_borders yes\n", \ -"draw_outline no\n", \ -"draw_shades no\n", \ -"use_xft yes\n", \ -"xftfont DejaVu Sans Mono:size=12\n", \ -"gap_x 5\n", \ -"gap_y 60\n", \ -"minimum_size 5 5\n", \ -"net_avg_samples 2\n", \ -"no_buffers yes\n", \ -"out_to_console no\n", \ -"out_to_stderr no\n", \ -"extra_newline no\n", \ -"own_window yes\n", \ -"own_window_class Conky\n", \ -"own_window_type desktop\n", \ -"stippled_borders 0\n", \ -"update_interval 1.0\n", \ -"uppercase no\n", \ -"use_spacer none\n", \ -"show_graph_scale no\n", \ -"show_graph_range no\n", \ -"\n", \ -"TEXT\n", \ -"${scroll 16 $nodename - $sysname $kernel on $machine | }\n", \ -"$hr\n", \ -"${color grey}Uptime:$color $uptime\n", \ -"${color grey}Frequency (in MHz):$color $freq\n", \ -"${color grey}Frequency (in GHz):$color $freq_g\n", \ -"${color grey}RAM Usage:$color $mem/$memmax - $memperc% ${membar 4}\n", \ -"${color grey}Swap Usage:$color $swap/$swapmax - $swapperc% ${swapbar 4}\n", \ -"${color grey}CPU Usage:$color $cpu% ${cpubar 4}\n", \ -"${color grey}Processes:$color $processes ${color grey}Running:$color $running_processes\n", \ -"$hr\n", \ -"${color grey}File systems:\n", \ -" / $color${fs_used /}/${fs_size /} ${fs_bar 6 /}\n", \ -"${color grey}Networking:\n", \ -"Up:$color ${upspeed eth0} ${color grey} - Down:$color ${downspeed eth0}\n", \ -"$hr\n", \ -"${color grey}Name PID CPU% MEM%\n", \ -"${color lightgrey} ${top name 1} ${top pid 1} ${top cpu 1} ${top mem 1}\n", \ -"${color lightgrey} ${top name 2} ${top pid 2} ${top cpu 2} ${top mem 2}\n", \ -"${color lightgrey} ${top name 3} ${top pid 3} ${top cpu 3} ${top mem 3}\n", \ -"${color lightgrey} ${top name 4} ${top pid 4} ${top cpu 4} ${top mem 4}\n", \ -NULL } - -#define print_defconfig() { \ - const char **__sp, *__s[] = defconfig; \ - for (__sp = __s; *__sp; __sp++) \ - printf("%s", *__sp); \ -} - -#endif /* __DEFCONFIG_H */ diff --git a/src/linux.c b/src/linux.c index 49e4768..abd60e6 100644 --- a/src/linux.c +++ b/src/linux.c @@ -84,6 +84,8 @@ #include #endif +#include + struct sysfs { int fd; int arg; @@ -1581,6 +1583,12 @@ static int acpi_design_capacity[MAX_BATTERY_COUNT]; //eg 4100 static int last_battery_volts[MAX_BATTERY_COUNT]; +//eg 78 +static unsigned char last_cell_radio_dbm; + +//eg 100 +static unsigned char last_cell_radio_percent; + //eg 35 static int last_battery_temp[MAX_BATTERY_COUNT]; @@ -1628,6 +1636,134 @@ int get_battery_idx(const char *bat) return idx; } +//void set_return_value(char *buffer, unsigned int n, int item, int idx); + +static int dbus_queue = 0; + +void set_dbus_retval(char *buffer, unsigned int n, int item); + +DBusConnection *connection; +DBusError error; +DBusMessage *message; +DBusMessageIter iter; +DBusBusType type; +int message_type; +DBusMessage *reply; +void get_dbus_stuff(char *buffer,unsigned int intMax_length, int item) +{ + char method[128]; + char path[128]; + char dest[128]; + if (dbus_queue > 0) + { + set_dbus_retval(buffer, intMax_length, item); + //snprintf(buffer,intMax_length,"%i",last_cell_radio_dbm); + return; + } + dbus_queue++;//prevent a queue from forming on these requests... +//fetch data from dbus, store in here as last_cell_radio_dbm +//return into buffer + + type = DBUS_BUS_SYSTEM; + message_type = DBUS_MESSAGE_TYPE_METHOD_CALL; +// print_reply = TRUE; +// print_reply_literal = FALSE; + int reply_timeout_ms = 5000; + dbus_error_init (&error); + connection = dbus_bus_get (type, &error); + if (connection == NULL) + { + fprintf (stderr, "Failed to open connection to %s message bus: %s\n", + (type == DBUS_BUS_SYSTEM) ? "system" : "session", + error.message); + dbus_error_free (&error); + exit (1); + } + switch(item){ + case DBUS_CELL_DBM: + snprintf(method,127,"get_signal_strength"); + snprintf(path,127,"/com/nokia/phone/net"); + snprintf(dest,127,"com.nokia.phone.net"); + message = dbus_message_new_method_call (dest,path,"Phone.Net",method); + dbus_message_set_auto_start (message, TRUE); + break; + case DBUS_CELL_PERCENT: + snprintf(method,127,"get_signal_strength"); + snprintf(path,127,"/com/nokia/phone/net"); + snprintf(dest,127,"com.nokia.phone.net"); + message = dbus_message_new_method_call (dest,path,"Phone.Net",method); + dbus_message_set_auto_start (message, TRUE); + break; + default: + fprintf (stderr, "invalid item type in get_dbus_stuff"); + break; + } + if (message == NULL) + { + fprintf (stderr, "Couldn't allocate D-Bus message\n"); + exit (1); + } + if (!dbus_message_set_destination (message, dest)) + { + fprintf (stderr, "Not enough memory\n"); + exit (1); + } + dbus_message_iter_init_append (message, &iter); + dbus_error_init (&error); + reply = dbus_connection_send_with_reply_and_block (connection, message, reply_timeout_ms, &error); + if (dbus_error_is_set (&error)) + { + fprintf (stderr, "Error %s: %s\n",error.name,error.message); + //exit (1);//if we set timeout to 30s or something i guess it's okay to exit on "no reply" cuz something is fu*ked; + } + if (reply) + { + DBusMessageIter iter; + dbus_message_iter_init (reply, &iter); + //int type = dbus_message_iter_get_arg_type(&iter); + int current_fieldnumber = 0; + while (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID) + { + //fprintf (stderr,"dbus-monitor too dumb to decipher arg type '%c'\n", type); + current_fieldnumber++; + if (current_fieldnumber == 1) + { + unsigned char val; + dbus_message_iter_get_basic(&iter, &val); + last_cell_radio_percent = val; + } + if (current_fieldnumber == 2) + { + unsigned char val; + dbus_message_iter_get_basic(&iter, &val); + last_cell_radio_dbm = val; + } + dbus_message_iter_next (&iter); + } + + dbus_message_unref (reply); + } + set_dbus_retval(buffer, intMax_length, item); + dbus_message_unref (message); + dbus_connection_unref (connection); + dbus_queue = 0;//reset to zero now that complete +} + +void set_dbus_retval(char *buffer, unsigned int intMax_length, int item) +{ + switch (item) { + case DBUS_CELL_DBM: + snprintf(buffer, intMax_length, "%d", last_cell_radio_dbm); + break; + case DBUS_CELL_PERCENT: + snprintf(buffer, intMax_length, "%d", last_cell_radio_percent); + break; + default: + fprintf (stderr, "invalid item type in set_dbus_retval"); + break; + } +} + void set_return_value(char *buffer, unsigned int n, int item, int idx); void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item) @@ -1708,7 +1844,7 @@ void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item) fclose(sysfs_bat_fp[idx]); sysfs_bat_fp[idx] = NULL; - + last_battery_volts[idx] = voltage; last_battery_temp[idx] = temp; @@ -1745,7 +1881,7 @@ void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item) // } /* discharging */ else if (present_rate > 0) { - + /* e.g. discharging 35% */ snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "discharging %i%%", remaining_capacity); @@ -1753,7 +1889,7 @@ void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item) sizeof(last_battery_time_str[idx]) - 1, "unknown"); /* e.g. 1h 12m */ // format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1, -// (long) (((float) remaining_capacity / present_rate) * 3600)); +// (long) (((float) remaining_capacity / present_rate) * 3600)); // else { // snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, // "discharging %d%%", @@ -1764,10 +1900,10 @@ void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item) } /* charged */ /* thanks to Lukas Zapletal */ - + if (remaining_capacity == 100) strcpy(last_battery_str[idx], "charged"); - + // else if (strncmp(charging_state, "Charged", 64) == 0 || strncmp(charging_state, "Full", 64) == 0) { // /* Below happens with the second battery on my X40, // * when the second one is empty and the first one @@ -2037,7 +2173,7 @@ int get_battery_perct(const char *bat) break; if (strncmp(buf, "POWER_SUPPLY_CAPACITY=", 22) == 0) { sscanf(buf, "POWER_SUPPLY_CAPACITY=%d", &remaining_capacity); - + // if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0) { // sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity); // } else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=",25) == 0) { @@ -2052,7 +2188,7 @@ int get_battery_perct(const char *bat) fclose(sysfs_bat_fp[idx]); sysfs_bat_fp[idx] = NULL; - } + } // else if (acpi_bat_fp[idx] != NULL) { // /* ACPI */ // /* read last full capacity if it's zero */ @@ -2097,7 +2233,7 @@ int get_battery_perct(const char *bat) return 0; } /* compute the battery percentage */ - last_battery_perct[idx] = + last_battery_perct[idx] = remaining_capacity; //(int) (((float) remaining_capacity / acpi_design_capacity[idx]) * 100); //if (last_battery_perct[idx] > 100) last_battery_perct[idx] = 100; diff --git a/src/text_object.h b/src/text_object.h index 4581c91..53a71fb 100644 --- a/src/text_object.h +++ b/src/text_object.h @@ -49,6 +49,8 @@ enum text_object_type { OBJ_battery_percent, OBJ_battery_bar, OBJ_battery_short, + OBJ_cell_radio_dbm, + OBJ_cell_radio_percent, #endif /* !__OpenBSD__ */ OBJ_buffers, OBJ_cached, -- 1.7.9.5