[RFC][Patch] DBus Service C library

Havoc Pennington hp at redhat.com
Fri Aug 10 13:21:52 PDT 2007


Hi,

Shahar Frank wrote:
 > So, I decided to
> implement simple and more "traditional" C oriented Server library. The
> library is not yet finished but I decided to contribute it hoping that I
> can get some valuable feedback, advices, comments, idea, etc. 

It's great to see work in this area. Did you see:
http://lists.freedesktop.org/archives/dbus/2007-July/008159.html

I think there are some ideas there you could incorporate.

The code there is a slightly more elaborated version of
http://svn.mugshot.org/local-export-daemon/trunk/src/hippo-dbus-helper.h

example usage is:
http://svn.mugshot.org/local-export-daemon/trunk/src/session-api.c

> I made an effort to do things in the "dbus way", but as I learned dbus
> low level only while writing this library, I missed many mechanisms and
> I have made quiet a few implementation mistakes. For example I
> implemented a new logging system, didn't use dbus portable function
> wrappers, and didn't use dbus internal abstract data structures, such as
> dbus link lists. I really want to have your opinions on these subjects
> (or any other). 

If you use the internal dbus utilities, we essentially have to make this 
a new feature of libdbus (in the same shared object). That is IMO 
probably the right thing to do.

During a testing/experimentation phase we could keep it separate, though 
it will mean some weird build system setup.

I think there's a lot of work left here to get a stable API.

> If you feel it may interest others, I will happy to move the project to
> your repository and work on it from there.

I'm not sure yet we'd definitely want it in the main dbus package, 
though it seems possible. The first step here would be to get an account 
on freedesktop.org in the dbus group, and then you could at least set up 
your own git repository if you want, and put things in CVS later if we 
decide that makes sense. To get an account you have to file a bug on 
bugs.freedesktop.org, the wiki probably has instructions on there somewhere.

Some code comments, probably premature:

> +struct dbus_type {
> +	char c;
> +	int size;
> +	int basic;
> +} dbus_types[] = {

There should be functions type_get_alignment() type_is_basic() etc. in 
the dbus code already, avoiding this array.

> +static char *log_level_str[] = {

As you say, should use the stuff in libdbus already for logging.

> +		free(service->err);
> +	service->err = strdup(msg);

You'd want to use dbus_free, _dbus_strdup, etc.

> +	for (ee = pp + DBUS_PARAM_MAX; pp < ee && *pp; pp++)
> +		if (!strcmp(name, (*pp)->name))
> +			return *pp;

libdbus code would use 2-space indent and strcmp == 0 rather than !strcmp

> +static int dbus_type_size(char const *sig)

the libdbus style would be

return_type
function_name()

on separate lines. Also, a static or internal function would start with 
_, like _dbus_type_get_size()

I think there's a get_alignment() already that pretty much does this.

> +	DBusParam *p = dbus_method_lookup_param(method, in, name);

libdbus coding style would not have a function call and variable decl on 
the same line.

> +	int sz;

libdbus style would not use abbreviations.

> +		memcpy(value, p->data, sz);

memcpy, strcpy, etc. are not allowed in libdbus style - DBusString is 
always used, see HACKING file.

> +	if (!(param = calloc(1, sizeof (*param))))

dbus_new0 is the calloc equivalent

> +	param->name = strdup(name);
> +	param->signature = strdup(signature);

Note here out-of-memory must be checked. (and use _dbus_strdup)

> +	if (!param || !param->name)
> +		return "NULL/unassigned param";

generally would use a DBusError rather than returning an error string

> +	if (in)		// strncat may write size + 1 char !!!
> +		strncat(method->insig, param->signature,
> sizeof(method->insig) -1);

DBusString rather than strncat


I'll stop with the style comments since you get the idea, and look at 
the API a bit:

> +   This Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License

(LGPL won't work btw, we were going to just do MIT/X11 for the dbus 
package in the future, but LGPL is incompatible with the current 
licensing if this is in the same package, I believe)

> +#ifndef _DBUS_SERVER_H
> +#define _DBUS_SERVER_H

Note there is already a DBusServer object and dbus-server.h

If you keep this a separate library, it should have a distinct namespace 
other than "dbus_"

If we fold it into libdbus, possibly it should have another namespace 
anyway, though I'm not sure I can think of one it might clarify things 
for developers to know that part of the API is "fundamental/low-level" 
and part is just convenience overlay / plain C binding.

> +#include <unistd.h>	// DBUS_PANIC.exit

System headers shouldn't be in library headers, since you can never 
remove them once added and apps may rely on them, and they can cause 
weird side effects for apps.

> +
> +enum {
> +	DBUS_SERVICE_LOG_NONE,
> +	DBUS_SERVICE_LOG_FATAL,
> +	DBUS_SERVICE_LOG_ERROR,
> +	DBUS_SERVICE_LOG_WARN,
> +	DBUS_SERVICE_LOG_INFO,
> +	DBUS_SERVICE_LOG_DEBUG,
> +	DBUS_SERVICE_LOG_VERBOSE,
> +
> +	DBUS_SERVICE_LOG_LAST_
> +};

The log facility should not be in public API.

> +typedef struct DBusService DBusService;

Most likely this should not be called DBusService. A service is a 
process that can be launched from a .service file. The library should 
probably not deal with that specifically much, instead usually we are 
interested in bus names. (Services can be launched by name, but not all 
names are owned by services.)

> +typedef char *(dbus_method_handler)(DBusMethod *method, DBusMessage
> *msg, void *user);

This would be DBusMethodHandler in the current API's style.

> +#define DBUS_PARAM_MAX 5
> +#define DBUS_SIG_STR_MAX 32

It would be better to avoid these maxes, I would say just have people 
spec the params as a single signature string. But otherwise, have a 
"const DBusParam*" in the method struct and require declaring the param 
array separately.

> +
> +enum {
> +	DBUS_METHOD_NORMAL = 0,
> +	DBUS_METHOD_SIGNAL = 1 << 0,
> +	DBUS_METHOD_SIGNAL_RECIEVER = 1 << 1,
> +};

I would not consider all of these methods, maybe
  DBUS_OBJECT_MEMBER_METHOD,
  DBUS_OBJECT_MEMBER_SIGNAL

A signal receiver to me is a different kind of thing, I can't figure out 
how to get it in the same enum.

The enum needs a typedef, like typedef enum {} DBusObjectMember;

> +struct DBusMethod {
> +	char *name;			// method member name

const char*

> +	DBusParam *inparam[DBUS_PARAM_MAX];	// in arguments
> +	DBusParam *outparam[DBUS_PARAM_MAX];	// return paramenters
> +
> +	char insig[DBUS_SIG_STR_MAX];	// method arguments signature
> +	char outsig[DBUS_SIG_STR_MAX];	// method return signature

These can just be const char*

> +	unsigned flags;			// type flags
> +	dbus_method_handler *handler;	// user handler function
> +	DBusInterface *interface;	// back pointer
> +	DBusSignal *signal;		// back pointer for signal
> methods
> +	void *user;			// user pointer
> +	struct DBusMethod *next;	// used by interface's methods
> list
> +};

I think you are stuffing the API and interesting-to-user-of-library 
stuff and internal-to-library stuff into one struct.

I would suggest having one struct that is used by the library user to 
declare their stuff in a static array, and a separate (private, not in 
the header) struct that is used for library-internal data structures.

I'm not sure you need most of these Signal/Interface/Param/etc. types in 
the public API - maybe look at how I did it in the stuff I linked to at 
the start of the mail.

> +char *dbus_service_emit_signal(DBusService *service, DBusMethod
> *emitter);

Signal emission in dbus is conceptually from an object, rather than from 
a bus name.

> +char *dbus_service_connect(DBusService * service, int bus_session);

enum typedef should be used, like DBusBusType, rather than int

> +static inline char *dbus_method_arg(DBusMethod *method, char *name,
> void *value)
> +{
> +	return dbus_method_get_param(method, name, 1, value);
> +}

inline isn't portable (unless we "#define inline" so it vanishes on some 
platforms), but I think the larger issue here is that an inline or macro 
ruins the ability to change the implementation in the future without 
rebuilding all apps. So I'd just make these normal library functions.

> +	if (method->flags & DBUS_METHOD_SIGNAL_RECIEVER)

I before E except after C ;-)

Anyway, this is a promising and high-value problem to tackle, nice work.

Havoc



More information about the dbus mailing list