From 93cff3d69fb705806d2af4fd6f29c497ea3192e0 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Thu, 21 Nov 2002 16:41:33 +0000 Subject: initial import of "dbus" skeleton --- AUTHORS | 1 + COPYING | 121 ++++++++++++ ChangeLog | 4 + INSTALL | 0 Makefile.am | 3 + NEWS | 1 + README | 1 + acconfig.h | 10 + autogen.sh | 70 +++++++ configure.in | 64 ++++++ dbus-1.0.pc.in | 11 ++ dbus/Makefile.am | 22 +++ dbus/dbus-hash.c | 0 dbus/dbus-message.c | 0 dbus/dbus.h | 0 doc/Makefile.am | 7 + doc/dcop-howto.txt | 559 ++++++++++++++++++++++++++++++++++++++++++++++++++++ server/Makefile.am | 15 ++ server/main.c | 8 + test/Makefile.am | 0 20 files changed, 897 insertions(+) create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README create mode 100644 acconfig.h create mode 100755 autogen.sh create mode 100644 configure.in create mode 100644 dbus-1.0.pc.in create mode 100644 dbus/Makefile.am create mode 100644 dbus/dbus-hash.c create mode 100644 dbus/dbus-message.c create mode 100644 dbus/dbus.h create mode 100644 doc/Makefile.am create mode 100644 doc/dcop-howto.txt create mode 100644 server/Makefile.am create mode 100644 server/main.c create mode 100644 test/Makefile.am diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..3bc119cb --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ +Havoc Pennington \ No newline at end of file diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..79037fa6 --- /dev/null +++ b/COPYING @@ -0,0 +1,121 @@ + Academic Free License + Version 1.2 + +This Academic Free License applies to any original work of authorship +(the "Original Work") whose owner (the "Licensor") has placed the +following notice immediately following the copyright notice for the +Original Work: + +Licensed under the Academic Free License version 1.2 + +Grant of License. Licensor hereby grants to any person obtaining a +copy of the Original Work ("You") a world-wide, royalty-free, +non-exclusive, perpetual, non-sublicenseable license (1) to use, copy, +modify, merge, publish, perform, distribute and/or sell copies of the +Original Work and derivative works thereof, and (2) under patent claims +owned or controlled by the Licensor that are embodied in the Original +Work as furnished by the Licensor, to make, use, sell and offer for +sale the Original Work and derivative works thereof, subject to the +following conditions. + +Attribution Rights. You must retain, in the Source Code of any +Derivative Works that You create, all copyright, patent or trademark +notices from the Source Code of the Original Work, as well as any +notices of licensing and any descriptive text identified therein as an +"Attribution Notice." You must cause the Source Code for any Derivative +Works that You create to carry a prominent Attribution Notice reasonably +calculated to inform recipients that You have modified the Original Work. + +Exclusions from License Grant. Neither the names of Licensor, nor the +names of any contributors to the Original Work, nor any of their +trademarks or service marks, may be used to endorse or promote products +derived from this Original Work without express prior written permission +of the Licensor. + +Warranty and Disclaimer of Warranty. Licensor warrants that the copyright +in and to the Original Work is owned by the Licensor or that the Original +Work is distributed by Licensor under a valid current license from the +copyright owner. Except as expressly stated in the immediately proceeding +sentence, the Original Work is provided under this License on an "AS IS" +BASIS and WITHOUT WARRANTY, either express or implied, including, without +limitation, the warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL +WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part +of this License. No license to Original Work is granted hereunder except +under this disclaimer. + +Limitation of Liability. Under no circumstances and under no legal theory, +whether in tort (including negligence), contract, or otherwise, shall the +Licensor be liable to any person for any direct, indirect, special, +incidental, or consequential damages of any character arising as a result +of this License or the use of the Original Work including, without +limitation, damages for loss of goodwill, work stoppage, computer failure +or malfunction, or any and all other commercial damages or losses. This +limitation of liability shall not apply to liability for death or personal +injury resulting from Licensor's negligence to the extent applicable law +prohibits such limitation. Some jurisdictions do not allow the exclusion or +limitation of incidental or consequential damages, so this exclusion and +limitation may not apply to You. + +License to Source Code. The term "Source Code" means the preferred form of +the Original Work for making modifications to it and all available +documentation describing how to modify the Original Work. Licensor hereby +agrees to provide a machine-readable copy of the Source Code of the Original +Work along with each copy of the Original Work that Licensor distributes. +Licensor reserves the right to satisfy this obligation by placing a +machine-readable copy of the Source Code in an information repository +reasonably calculated to permit inexpensive and convenient access by You for +as long as Licensor continues to distribute the Original Work, and by +publishing the address of that information repository in a notice immediately +following the copyright notice that applies to the Original Work. + +Mutual Termination for Patent Action. This License shall terminate +automatically and You may no longer exercise any of the rights granted to You +by this License if You file a lawsuit in any court alleging that any OSI +Certified open source software that is licensed under any license containing +this "Mutual Termination for Patent Action" clause infringes any patent +claims that are essential to use that software. + +Right to Use. You may use the Original Work in all ways not otherwise +restricted or conditioned by this License or by law, and Licensor promises +not to interfere with or be responsible for such uses by You. + +This license is Copyright (C) 2002 Lawrence E. Rosen. All rights reserved. +Permission is hereby granted to copy and distribute this license without +modification. This license may not be modified without the express written +permission of its copyright owner. + +-- +END OF LICENSE. The following is intended to describe the essential +differences between the Academic Free License (AFL) version 1.0 and other +open source licenses: + +The Academic Free License is similar to the BSD, MIT, UoI/NCSA and Apache +licenses in many respects but it is intended to solve a few problems with +those licenses. + +* The AFL is written so as to make it clear what software is being +licensed (by the inclusion of a statement following the copyright notice +in the software). This way, the license functions better than a template +license. The BSD, MIT and UoI/NCSA licenses apply to unidentified software. + +* The AFL contains a complete copyright grant to the software. The BSD +and Apache licenses are vague and incomplete in that respect. + +* The AFL contains a complete patent grant to the software. The BSD, MIT, +UoI/NCSA and Apache licenses rely on an implied patent license and contain +no explicit patent grant. + +* The AFL makes it clear that no trademark rights are granted to the +licensor's trademarks. The Apache license contains such a provision, but the +BSD, MIT and UoI/NCSA licenses do not. + +* The AFL includes the warranty by the licensor that it either owns the +copyright or that it is distributing the software under a license. None of +the other licenses contain that warranty. All other warranties are disclaimed, +as is the case for the other licenses. + +* The AFL is itself copyrighted (with the right granted to copy and distribute +without modification). This ensures that the owner of the copyright to the +license will control changes. The Apache license contains a copyright notice, +but the BSD, MIT and UoI/NCSA licenses do not. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 00000000..aa6579d3 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,4 @@ +2002-11-21 Havoc Pennington + + * Initial module creation + diff --git a/INSTALL b/INSTALL new file mode 100644 index 00000000..e69de29b diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 00000000..e35a6de5 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,3 @@ + +SUBDIRS=dbus server test doc + diff --git a/NEWS b/NEWS new file mode 100644 index 00000000..047ccd09 --- /dev/null +++ b/NEWS @@ -0,0 +1 @@ +No news. \ No newline at end of file diff --git a/README b/README new file mode 100644 index 00000000..7ce279d3 --- /dev/null +++ b/README @@ -0,0 +1 @@ +D-BUS is a simple IPC library based on messages. diff --git a/acconfig.h b/acconfig.h new file mode 100644 index 00000000..e100ec29 --- /dev/null +++ b/acconfig.h @@ -0,0 +1,10 @@ +#undef PACKAGE +#undef VERSION +#undef HAVE_CATGETS +#undef HAVE_GETTEXT +#undef HAVE_LC_MESSAGES +#undef HAVE_STPCPY +#undef ENABLE_NLS +#undef HAVE_PTHREAD_H +#undef GETTEXT_PACKAGE +#undef SANE_MALLOC_PROTOS diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 00000000..3b74d828 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,70 @@ +#!/bin/sh +# Run this to generate all the initial makefiles, etc. + +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. + +ORIGDIR=`pwd` +cd $srcdir + +PROJECT=dbus +TEST_TYPE=-f +FILE=dbus-1.0.pc.in + +DIE=0 + +(autoconf --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "You must have autoconf installed to compile $PROJECT." + echo "Download the appropriate package for your distribution," + echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/" + DIE=1 +} + +AUTOMAKE=automake-1.6 +ACLOCAL=aclocal-1.6 + +($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || { + AUTOMAKE=automake + ACLOCAL=aclocal +} + +($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "You must have automake installed to compile $PROJECT." + echo "Get ftp://ftp.cygnus.com/pub/home/tromey/automake-1.2d.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 +} + +if test "$DIE" -eq 1; then + exit 1 +fi + +test $TEST_TYPE $FILE || { + echo "You must run this script in the top-level $PROJECT directory" + exit 1 +} + +if test -z "$*"; then + echo "I am going to run ./configure with no arguments - if you wish " + echo "to pass any to it, please specify them on the $0 command line." +fi + +libtoolize --copy --force + +echo $ACLOCAL $ACLOCAL_FLAGS +$ACLOCAL $ACLOCAL_FLAGS + +# optionally feature autoheader +(autoheader --version) < /dev/null > /dev/null 2>&1 && autoheader + +$AUTOMAKE -a $am_opt +autoconf || echo "autoconf failed - version 2.5x is probably required" + +cd $ORIGDIR + +$srcdir/configure --enable-maintainer-mode "$@" + +echo +echo "Now type 'make' to compile $PROJECT." diff --git a/configure.in b/configure.in new file mode 100644 index 00000000..18f8f967 --- /dev/null +++ b/configure.in @@ -0,0 +1,64 @@ +AC_INIT(dbus/dbus.h) + +AM_CONFIG_HEADER(config.h) + +AM_INIT_AUTOMAKE(dbus, 0.1) + +# Honor aclocal flags +ACLOCAL="$ACLOCAL $ACLOCAL_FLAGS" + +GETTEXT_PACKAGE=dbus-1 +AC_SUBST(GETTEXT_PACKAGE) +AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE") + +AM_MAINTAINER_MODE + +AC_PROG_CC +AC_ISC_POSIX +AC_HEADER_STDC +AC_ARG_PROGRAM +AM_PROG_LIBTOOL + +AC_ARG_ENABLE(qt, [ --disable-qt disable Qt-friendly client library],enable_qt=no,enable_qt=yes) +AC_ARG_ENABLE(glib, [ --disable-glib disable GLib-friendly client library],enable_glib=no,enable_glib=yes) + +changequote(,)dnl +if test "x$GCC" = "xyes"; then + case " $CFLAGS " in + *[\ \ ]-Wall[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -Wall" ;; + esac +fi +changequote([,])dnl + +AC_CHECK_SIZEOF(char) +AC_CHECK_SIZEOF(short) +AC_CHECK_SIZEOF(long) +AC_CHECK_SIZEOF(int) +AC_CHECK_SIZEOF(void *) +AC_CHECK_SIZEOF(long long) +AC_CHECK_SIZEOF(__int64) + +## byte order +AC_C_BIGENDIAN + +AC_CHECK_FUNCS(vsnprintf vasprintf) + +DBUS_CLIENT_CFLAGS= +DBUS_CLIENT_LIBS= +AC_SUBST(DBUS_CLIENT_CFLAGS) +AC_SUBST(DBUS_CLIENT_LIBS) + +DBUS_SERVER_CFLAGS= +DBUS_SERVER_LIBS= +AC_SUBST(DBUS_SERVER_CFLAGS) +AC_SUBST(DBUS_SERVER_LIBS) + +AC_OUTPUT([ +Makefile +dbus/Makefile +server/Makefile +test/Makefile +doc/Makefile +dbus-1.0.pc +]) diff --git a/dbus-1.0.pc.in b/dbus-1.0.pc.in new file mode 100644 index 00000000..2df0a7cf --- /dev/null +++ b/dbus-1.0.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: dbus +Description: Free desktop message bus +Version: @VERSION@ +Libs: -L${libdir} -ldbus-1 +Cflags: -I${includedir}/dbus-1.0 + diff --git a/dbus/Makefile.am b/dbus/Makefile.am new file mode 100644 index 00000000..09dd84f8 --- /dev/null +++ b/dbus/Makefile.am @@ -0,0 +1,22 @@ + +INCLUDES=-I$(top_srcdir) $(DBUS_CLIENT_CFLAGS) + +dbusincludedir=$(includedir)/dbus-1.0/dbus + +lib_LTLIBRARIES=libdbus-1.la + +dbusinclude_HEADERS= \ + dbus.h + +libdbus_1_la_SOURCES= \ + dbus-message.c + +noinst_LTLIBRARIES=libdbus-convenience.la + +libdbus_convenience_la_SOURCES= \ + dbus-hash.c + +libdbus_1_la_LIBADD= $(DBUS_CLIENT_LIBS) libdbus-convenience.la +## don't export symbols that start with "_" (we use this +## convention for internal symbols) +libdbus_1_la_LDFLAGS= -export-symbols-regex "^[[^_]].*" diff --git a/dbus/dbus-hash.c b/dbus/dbus-hash.c new file mode 100644 index 00000000..e69de29b diff --git a/dbus/dbus-message.c b/dbus/dbus-message.c new file mode 100644 index 00000000..e69de29b diff --git a/dbus/dbus.h b/dbus/dbus.h new file mode 100644 index 00000000..e69de29b diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100644 index 00000000..d84c70be --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1,7 @@ +EXTRA_DIST= \ + dcop-howto.txt \ + fdmb-design.txt \ + fdmb-messages.txt \ + fdmb-properties.txt \ + message-delivery.txt \ + message-matching.txt diff --git a/doc/dcop-howto.txt b/doc/dcop-howto.txt new file mode 100644 index 00000000..dfd3bcf8 --- /dev/null +++ b/doc/dcop-howto.txt @@ -0,0 +1,559 @@ + DCOP: Desktop COmmunications Protocol + + Preston Brown + October 14, 1999 + + Revised and extended by Matthias Ettrich + Mar 29, 2000 + + Extended with DCOP Signals by Waldo Bastian + Feb 19, 2001 + + +Motivation and Background: +-------------------------- + +The motivation behind building a protocol like DCOP is simple. For +the past year, we have been attempting to enable interprocess +communication between KDE applications. KDE already has an extremely +simple IPC mechanism called KWMcom, which is (was!) used for communicating +between the panel and the window manager for instance. It is about as +simple as it gets, passing messages via X Atoms. For this reason it +is limited in the size and complexity of the data that can be passed +(X atoms must be small to remain efficient) and it also makes it so +that X is required. CORBA was thought to be a more effective IPC/RPC +solution. However, after a year of attempting to make heavy use of +CORBA in KDE, we have realized that it is a bit slow and memory +intensive for simple use. It also has no authentication available. + +What we really needed was an extremely simple protocol with basic +authorization, along the lines of MIT-MAGIC-COOKIE, as used by X. It +would not be able to do NEARLY what CORBA was able to do, but for the +simple tasks required it would be sufficient. Some examples of such +tasks might be an application sending a message to the panel saying, +"I have started, stop displaying the 'application starting' wait +state," or having a new application that starts query to see if any +other applications of the same name are running. If they are, simply +call a function on the remote application to create a new window, +rather than starting a new process. + +Implementation: +--------------- + +DCOP is a simple IPC/RPC mechanism built to operate over sockets. +Either unix domain sockets or tcp/ip sockets are supported. DCOP is +built on top of the Inter Client Exchange (ICE) protocol, which comes +standard as a part of X11R6 and later. It also depends on Qt, but +beyond that it does not require any other libraries. Because of this, +it is extremely lightweight, enabling it to be linked into all KDE +applications with low overhead. + +Model: +------ + +The model is simple. Each application using DCOP is a client. They +communicate to each other through a DCOP server, which functions like +a traffic director, dispatching messages/calls to the proper +destinations. All clients are peers of each other. + +Two types of actions are possible with DCOP: "send and forget" +messages, which do not block, and "calls," which block waiting for +some data to be returned. + +Any data that will be sent is serialized (marshalled, for you CORBA +types) using the built-in QDataStream operators available in all of +the Qt classes. This is fast and easy. In fact it's so little work +that you can easily write the marshalling code by hand. In addition, +there's a simple IDL-like compiler available (dcopidl and dcopidl2cpp) +that generates stubs and skeletons for you. Using the dcopidl compiler +has the additional benefit of type safety. + +This HOWTO describes the manual method first and covers the dcopidl +compiler later. + +Establishing the Connection: +---------------------------- + +KApplication has gained a method called "KApplication::dcopClient()" +which returns a pointer to a DCOPClient instance. The first time this +method is called, the client class will be created. DCOPClients have +unique identifiers attached to them which are based on what +KApplication::name() returns. In fact, if there is only a single +instance of the program running, the appId will be equal to +KApplication::name(). + +To actually enable DCOP communication to begin, you must use +DCOPClient::attach(). This will attempt to attach to the DCOP server. +If no server is found or there is any other type of error, attach() +will return false. KApplication will catch a dcop signal and display an +appropriate error message box in that case. + +After connecting with the server via DCOPClient::attach(), you need to +register this appId with the server so it knows about you. Otherwise, +you are communicating anonymously. Use the +DCOPClient::registerAs(const QCString &name) to do so. In the simple +case: + +/* + * returns the appId that is actually registered, which _may_ be + * different from what you passed + */ +appId = client->registerAs(kApp->name()); + +If you never retrieve the DCOPClient pointer from KApplication, the +object will not be created and thus there will be no memory overhead. + +You may also detach from the server by calling DCOPClient::detach(). +If you wish to attach again you will need to re-register as well. If +you only wish to change the ID under which you are registered, simply +call DCOPClient::registerAs() with the new name. + +KUniqueApplication automatically registers itself to DCOP. If you +are using KUniqueApplication you should not attach or register +yourself, this is already done. The appId is by definition +equal to kapp->name(). You can retrieve the registered DCOP client +by calling kapp->dcopClient(). + +Sending Data to a Remote Application: +------------------------------------- + +To actually communicate, you have one of two choices. You may either +call the "send" or the "call" method. Both methods require three +identification parameters: an application identifier, a remote object, +a remote function. Sending is asynchronous (i.e. it returns immediately) +and may or may not result in your own application being sent a message at +some point in the future. Then "send" requires one and "call" requires +two data parameters. + +The remote object must be specified as an object hierarchy. That is, +if the toplevel object is called "fooObject" and has the child +"barObject", you would reference this object as "fooObject/barObject". +Functions must be described by a full function signature. If the +remote function is called "doIt", and it takes an int, it would be +described as "doIt(int)". Please note that the return type is not +specified here, as it is not part of the function signature (or at +least the C++ understanding of a function signature). You will get +the return type of a function back as an extra parameter to +DCOPClient::call(). See the section on call() for more details. + +In order to actually get the data to the remote client, it must be +"serialized" via a QDataStream operating on a QByteArray. This is how +the data parameter is "built". A few examples will make clear how this +works. + +Say you want to call "doIt" as described above, and not block (or wait +for a response). You will not receive the return value of the remotely +called function, but you will not hang while the RPC is processed either. +The return value of send() indicates whether DCOP communication succeeded +or not. + +QByteArray data; +QDataStream arg(data, IO_WriteOnly); +arg << 5; +if (!client->send("someAppId", "fooObject/barObject", "doIt(int)", + data)) + qDebug("there was some error using DCOP."); + +OK, now let's say we wanted to get the data back from the remotely +called function. You have to execute a call() instead of a send(). +The returned value will then be available in the data parameter "reply". +The actual return value of call() is still whether or not DCOP +communication was successful. + +QByteArray data, replyData; +QCString replyType; +QDataStream arg(data, IO_WriteOnly); +arg << 5; +if (!client->call("someAppId", "fooObject/barObject", "doIt(int)", + data, replyType, replyData)) + qDebug("there was some error using DCOP."); +else { + QDataStream reply(replyData, IO_ReadOnly); + if (replyType == "QString") { + QString result; + reply >> result; + print("the result is: %s",result.latin1()); + } else + qDebug("doIt returned an unexpected type of reply!"); +} + +N.B.: You cannot call() a method belonging to an application which has +registered with an unique numeric id appended to its textual name (see +dcopclient.h for more info). In this case, DCOP would not know which +application it should connect with to call the method. This is not an issue +with send(), as you can broadcast to all applications that have registered +with appname- by using a wildcard (e.g. 'konsole-*'), which +will send your signal to all applications called 'konsole'. + +Receiving Data via DCOP: +------------------------ + +Currently the only real way to receive data from DCOP is to multiply +inherit from the normal class that you are inheriting (usually some +sort of QWidget subclass or QObject) as well as the DCOPObject class. +DCOPObject provides one very important method: DCOPObject::process(). +This is a pure virtual method that you must implement in order to +process DCOP messages that you receive. It takes a function +signature, QByteArray of parameters, and a reference to a QByteArray +for the reply data that you must fill in. + +Think of DCOPObject::process() as a sort of dispatch agent. In the +future, there will probably be a precompiler for your sources to write +this method for you. However, until that point you need to examine +the incoming function signature and take action accordingly. Here is +an example implementation. + +bool BarObject::process(const QCString &fun, const QByteArray &data, + QCString &replyType, QByteArray &replyData) +{ + if (fun == "doIt(int)") { + QDataStream arg(data, IO_ReadOnly); + int i; // parameter + arg >> i; + QString result = self->doIt (i); + QDataStream reply(replyData, IO_WriteOnly); + reply << result; + replyType = "QString"; + return true; + } else { + qDebug("unknown function call to BarObject::process()"); + return false; + } +} + +Receiving Calls and processing them: +------------------------------------ + +If your applications is able to process incoming function calls +right away the above code is all you need. When your application +needs to do more complex tasks you might want to do the processing +out of 'process' function call and send the result back later when +it becomes available. + +For this you can ask your DCOPClient for a transactionId. You can +then return from the 'process' function and when the result is +available finish the transaction. In the mean time your application +can receive incoming DCOP function calls from other clients. + +Such code could like this: + +bool BarObject::process(const QCString &fun, const QByteArray &data, + QCString &, QByteArray &) +{ + if (fun == "doIt(int)") { + QDataStream arg(data, IO_ReadOnly); + int i; // parameter + arg >> i; + QString result = self->doIt(i); + + DCOPClientTransaction *myTransaction; + myTransaction = kapp->dcopClient()->beginTransaction(); + + // start processing... + // Calls slotProcessingDone when finished. + startProcessing( myTransaction, i); + + return true; + } else { + qDebug("unknown function call to BarObject::process()"); + return false; + } +} + +slotProcessingDone(DCOPClientTransaction *myTransaction, const QString &result) +{ + QCString replyType = "QString"; + QByteArray replyData; + QDataStream reply(replyData, IO_WriteOnly); + reply << result; + kapp->dcopClient()->endTransaction( myTransaction, replyType, replyData ); +} + +DCOP Signals +------------ + +Sometimes a component wants to send notifications via DCOP to other +components but does not know which components will be interested in these +notifications. One could use a broadcast in such a case but this is a very +crude method. For a more sophisticated method DCOP signals have been invented. + +DCOP signals are very similair to Qt signals, there are some differences +though. A DCOP signal can be connected to a DCOP function. Whenever the DCOP +signal gets emitted, the DCOP functions to which the signal is connected are +being called. DCOP signals are, just like Qt signals, one way. They do not +provide a return value. + +A DCOP signal originates from a DCOP Object/DCOP Client combination (sender). +It can be connected to a function of another DCOP Object/DCOP Client +combination (receiver). + +There are two major differences between connections of Qt signals and +connections of DCOP signals. In DCOP, unlike Qt, a signal connections can +have an anonymous sender and, unlike Qt, a DCOP signal connection can be +non-volatile. + +With DCOP one can connect a signal without specifying the sending DCOP Object +or DCOP Client. In that case signals from any DCOP Object and/or DCOP Client +will be delivered. This allows the specification of certain events without +tying oneself to a certain object that implementes the events. + +Another DCOP feature are so called non-volatile connections. With Qt signal +connections, the connection gets deleted when either sender or receiver of +the signal gets deleted. A volatile DCOP signal connection will behave the +same. However, a non-volatile DCOP signal connection will not get deleted +when the sending object gets deleted. Once a new object gets created with +the same name as the original sending object, the connection will be restored. +There is no difference between the two when the receiving object gets deleted, +in that case the signal connection will always be deleted. + +A receiver can create a non-volatile connection while the sender doesn't (yet) +exist. An anonymous DCOP connection should always be non-volatile. + +The following example shows how KLauncher emits a signal whenever it notices +that an application that was started via KLauncher terminates. + + QByteArray params; + QDataStream stream(params, IO_WriteOnly); + stream << pid; + kapp->dcopClient()->emitDCOPSignal("clientDied(pid_t)", params); + +The task manager of the KDE panel connects to this signal. It uses an +anonymous connection (it doesn't require that the signal is being emitted +by KLauncher) that is non-volatile: + + connectDCOPSignal(0, 0, "clientDied(pid_t)", "clientDied(pid_t)", false); + +It connects the clientDied(pid_t) signal to its own clientDied(pid_t) DCOP +function. In this case the signal and the function to call have the same name. +This isn't needed as long as the arguments of both signal and receiving function +match. The receiving function may ignore one or more of the trailing arguments +of the signal. E.g. it is allowed to connect the clientDied(pid_t) signal to +a clientDied(void) DCOP function. + +Using the dcopidl compiler +--------------------- + +dcopidl makes setting up a DCOP server easy. Instead of having to implement +the process() method and unmarshalling (retrieving from QByteArray) parameters +manually, you can let dcopidl create the necessary code on your behalf. + +This also allows you to describe the interface for your class in a +single, separate header file. + +Writing an IDL file is very similar to writing a normal C++ header. An +exception is the keyword 'ASYNC'. It indicates that a call to this +function shall be processed asynchronously. For the C++ compiler, it +expands to 'void'. + +Example: + +#ifndef MY_INTERFACE_H +#define MY_INTERFACE_H + +#include + +class MyInterface : virtual public DCOPObject +{ + K_DCOP + + k_dcop: + + virtual ASYNC myAsynchronousMethod(QString someParameter) = 0; + virtual QRect mySynchronousMethod() = 0; +}; + +#endif + +As you can see, you're essentially declaring an abstract base class, which +virtually inherits from DCOPObject. + +If you're using the standard KDE build scripts, then you can simply +add this file (which you would call MyInterface.h) to your sources +directory. Then you edit your Makefile.am, adding 'MyInterface.skel' +to your SOURCES list and MyInterface.h to include_HEADERS. + +The build scripts will use dcopidl to parse MyInterface.h, converting +it to an XML description in MyInterface.kidl. Next, a file called +MyInterface_skel.cpp will automatically be created, compiled and +linked with your binary. + +The next thing you have to do is to choose which of your classes will +implement the interface described in MyInterface.h. Alter the inheritance +of this class such that it virtually inherits from MyInterface. Then +add declarations to your class interface similar to those on MyInterface.h, +but virtual, not pure virtual. + +Example: + +class MyClass: public QObject, virtual public MyInterface +{ + Q_OBJECT + + public: + MyClass(); + ~MyClass(); + + ASYNC myAsynchronousMethod(QString someParameter); + QRect mySynchronousMethod(); +}; + +Note: (Qt issue) Remember that if you are inheriting from QObject, you must +place it first in the list of inherited classes. + +In the implementation of your class' ctor, you must explicitly initialize +those classes from which you are inheriting from. This is, of course, good +practise, but it is essential here as you need to tell DCOPObject the name of +the interface which your are implementing. + +Example: + +MyClass::MyClass() + : QObject(), + DCOPObject("MyInterface") +{ + // whatever... +} + +Now you can simply implement the methods you have declared in your interface, +exactly the same as you would normally. + +Example: + +void MyClass::myAsynchronousMethod(QString someParameter) +{ + qDebug("myAsyncMethod called with param `" + someParameter + "'"); +} + + +It is not necessary (though very clean) to define an interface as an +abstract class of its own, like we did in the example above. We could +just as well have defined a k_dcop section directly within MyClass: + +class MyClass: public QObject, virtual public DCOPObject +{ + Q_OBJECT + K_DCOP + + public: + MyClass(); + ~MyClass(); + + k_dcop: + ASYNC myAsynchronousMethod(QString someParameter); + QRect mySynchronousMethod(); +}; + +In addition to skeletons, dcopidl2cpp also generate stubs. Those make +it easy to call a DCOP interface without doing the marshalling +manually. To use a stub, add MyInterface.stub to the SOURCES list of +your Makefile.am. The stub class will then be called MyInterface_stub. + +Conclusion: +----------- + +Hopefully this document will get you well on your way into the world +of inter-process communication with KDE! Please direct all comments +and/or suggestions to Preston Brown and Matthias +Ettrich . + + +Inter-user communication +------------------------ + +Sometimes it might be interesting to use DCOP between processes +belonging to different users, e.g. a frontend process running +with the user's id, and a backend process running as root. + +To do this, two steps have to be taken: + +a) both processes need to talk to the same DCOP server +b) the authentication must be ensured + +For the first step, you simply pass the server address (as +found in .DCOPserver) to the second process. For the authentication, +you can use the ICEAUTHORITY environment variable to tell the +second process where to find the authentication information. +(Note that this implies that the second process is able to +read the authentication file, so it will probably only work +if the second process runs as root. If it should run as another +user, a similar approach to what kdesu does with xauth must +be taken. In fact, it would be a very good idea to add DCOP +support to kdesu!) + +For example + +ICEAUTHORITY=~user/.ICEauthority kdesu root -c kcmroot -dcopserver `cat ~user/.DCOPserver` + +will, after kdesu got the root password, execute kcmroot as root, talking +to the user's dcop server. + + +NOTE: DCOP communication is not encrypted, so please do not +pass important information around this way. + + +Performance Tests: +------------------ +A few back-of-the-napkin tests folks: + +Code: + +#include + +int main(int argc, char **argv) +{ + KApplication *app; + + app = new KApplication(argc, argv, "testit"); + return app->exec(); +} + +Compiled with: + +g++ -O2 -o testit testit.cpp -I$QTDIR/include -L$QTDIR/lib -lkdecore + +on Linux yields the following memory use statistics: + +VmSize: 8076 kB +VmLck: 0 kB +VmRSS: 4532 kB +VmData: 208 kB +VmStk: 20 kB +VmExe: 4 kB +VmLib: 6588 kB + +If I create the KApplication's DCOPClient, and call attach() and +registerAs(), it changes to this: + +VmSize: 8080 kB +VmLck: 0 kB +VmRSS: 4624 kB +VmData: 208 kB +VmStk: 20 kB +VmExe: 4 kB +VmLib: 6588 kB + +Basically it appears that using DCOP causes 100k more memory to be +resident, but no more data or stack. So this will be shared between all +processes, right? 100k to enable DCOP in all apps doesn't seem bad at +all. :) + +OK now for some timings. Just creating a KApplication and then exiting +(i.e. removing the call to KApplication::exec) takes this much time: + +0.28user 0.02system 0:00.32elapsed 92%CPU (0avgtext+0avgdata 0maxresident)k +0inputs+0outputs (1084major+62minor)pagefaults 0swaps + +I.e. about 1/3 of a second on my PII-233. Now, if we create our DCOP +object and attach to the server, it takes this long: + +0.27user 0.03system 0:00.34elapsed 87%CPU (0avgtext+0avgdata 0maxresident)k +0inputs+0outputs (1107major+65minor)pagefaults 0swaps + +I.e. about 1/3 of a second. Basically DCOPClient creation and attaching +gets lost in the statistical variation ("noise"). I was getting times +between .32 and .48 over several runs for both of the example programs, so +obviously system load is more relevant than the extra two calls to +DCOPClient::attach and DCOPClient::registerAs, as well as the actual +DCOPClient constructor time. + diff --git a/server/Makefile.am b/server/Makefile.am new file mode 100644 index 00000000..33049961 --- /dev/null +++ b/server/Makefile.am @@ -0,0 +1,15 @@ + +INCLUDES=-I$(top_srcdir) $(DBUS_SERVER_CFLAGS) \ + -DDAEMON_NAME=\"dbus-daemon-1\" + +EFENCE= + +bin_PROGRAMS=dbus-daemon-1 + +dbus_daemon_1_SOURCES= \ + main.c + +dbus_daemon_1_LDADD= \ + $(EFENCE) \ + $(DBUS_SERVER_LIBS) \ + $(top_builddir)/dbus/libdbus-convenience.la diff --git a/server/main.c b/server/main.c new file mode 100644 index 00000000..1025a0e7 --- /dev/null +++ b/server/main.c @@ -0,0 +1,8 @@ + +int +main (int argc, char **argv) +{ + + + return 0; +} diff --git a/test/Makefile.am b/test/Makefile.am new file mode 100644 index 00000000..e69de29b -- cgit