dbus-service example program

A.M. Kuchling amk@amk.ca
Wed, 10 Sep 2003 09:54:11 -0400


I'm evaluating D-BUS for use in a commercial system.  There's an
example program to send messages but not to display messages
received by a service, so I took dbus-send.c/dbus-monitor.c and 
produced dbus-service.c.  

You run it like this:

    dbus-service [--handle-ping] service-name1 servicename2 ...

It registers services for service-name1, service-name2, ... and then
prints all messages received to standard output separated by a blank
line.  If --handle-ping is supplied, it will generate replies to ping
messages; otherwise pings are displayed like an

This should make it possible to write D-BUS services in Python or
other scripting languages without a D-BUS binding; a script could read
and parse the output of dbus-service and generate new messages with
dbus-send.  (It couldn't generate replies, though; dbus-send would
need a '--reply <serial-num>' option to make that work.)

Because the code is largely copied from dbus-monitor, the code may be
suboptimal and I'd like to get suggestions for improving it.  In
particular, is it necessary to use the glib main loop in order to
receive messages?

Do I need to sign an assignment so this can be included in the dbus
tree?

--amk

/* -*- mode: C; c-file-style: "gnu" -*- */
/* dbus-service.c  Utility program to display messages sent to a service.
 *
 * Copyright (C) 2003 A.M. Kuchling <amk@amk.ca>
 *
 * 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include <glib.h>
#include <dbus/dbus.h>
 /* Don't copy this, for programs outside the dbus tree it's dbus/dbus-glib.h */
#include <glib/dbus-glib.h>

#include "dbus-print-message.h"

int handle_ping;
int num_services;
char *service_names[100];

static void
usage (char *name, int ecode)
{
  fprintf (stderr, "Usage: %s [--help] [--system | --session] [--handle-ping] service-name1 servicename2 ...\n", name);
  exit (ecode);
}



static DBusHandlerResult
handler_func (DBusMessageHandler *handler,
 	      DBusConnection     *connection,
	      DBusMessage        *message,
	      void               *user_data)
{
  int i, matching;
  
  if (dbus_message_has_name (message, DBUS_MESSAGE_LOCAL_DISCONNECT))
    exit (0);
  if (handle_ping && dbus_message_has_name(message, 
					   "org.freedesktop.Peer.Ping"))
    {
      DBusMessage *reply;
      fprintf(stderr, "Ping received from %s\n",
	      dbus_message_get_sender(message));
      reply = dbus_message_new ("org.freedesktop.Peer.Ping", 
				dbus_message_get_sender(message));
      dbus_message_set_reply_serial(reply,
				    dbus_message_get_serial(message));
      dbus_connection_send (connection, reply, NULL);
      dbus_connection_flush (connection);
      dbus_message_unref(reply);
      return DBUS_HANDLER_RESULT_REMOVE_MESSAGE;
    }
  
  /* Test if this message is directed to one of the services being monitored */
  matching = FALSE;
  for(i=0; i<num_services; i++) {
    if (dbus_message_has_destination(message, service_names[i])) {
      matching = TRUE;
    }
  }
  if (matching)
    {
      printf("service=%s\n", dbus_message_get_destination(message));
      print_message(message);
      printf("\n"); /* Add blank line to separate messages */
      fflush(stdout);
    }

  return DBUS_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
}

int
main (int argc, char *argv[])
{
  DBusConnection *connection;
  DBusError error;
  DBusMessage *message;
  DBusMessageIter iter;
  DBusMessageHandler *handler;
  GMainLoop *loop;

  int i;
  DBusBusType type = DBUS_BUS_SESSION;
  char *name = NULL;

  if (argc < 2)
    usage (argv[0], 1);

  handle_ping = FALSE;
  for (i = 1; i < argc && name == NULL; i++)
    {
      char *arg = argv[i];

      if (strcmp (arg, "--system") == 0)
	type = DBUS_BUS_SYSTEM;
      else if (strcmp (arg, "--session") == 0)
	type = DBUS_BUS_SESSION;
      else if (strcmp (arg, "--handle-ping") == 0)
        handle_ping = TRUE;
      else if (!strcmp(arg, "--help"))
	usage (argv[0], 0);
      else if (arg[0] == '-')
	usage (argv[0], 1);
      else {
	break;
      }
    }

  if (i == argc)
    usage (argv[0], 1);

  loop = g_main_loop_new (NULL, FALSE);
  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);
    }

  /* Allocate services */

  num_services = 0;
  for(; i<argc; i++, num_services++) {
    DBusMessage *reply;
    dbus_uint32_t reply_code;

    service_names[num_services] = argv[i];
    message = dbus_message_new ("org.freedesktop.DBus.AcquireService", 
				"org.freedesktop.DBus");
    if (message == NULL)
      {
	fprintf (stderr, "Couldn't allocate D-BUS message\n");
	exit (1);
      }
    dbus_message_append_iter_init (message, &iter);
    dbus_message_iter_append_string (&iter, argv[i]);
    dbus_message_iter_append_uint32 (&iter, 
				     DBUS_SERVICE_FLAG_REPLACE_EXISTING);

    reply = dbus_connection_send_with_reply_and_block (connection,
						       message, -1,
						       &error);
    if (dbus_error_is_set (&error))
      {
	fprintf (stderr, "Error: %s\n",
		 error.message);
	exit (1);
      }

    /* Check reply contents */
    dbus_message_iter_init (reply, &iter);
    assert (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_UINT32);
    reply_code = dbus_message_iter_get_uint32 (&iter);
    if (reply_code == DBUS_SERVICE_REPLY_IN_QUEUE)
      printf("Service %s: %s placed in queue\n", argv[i], argv[0]);
    else if (reply_code == DBUS_SERVICE_REPLY_SERVICE_EXISTS)
      printf("Service %s: already exists (shouldn't happen)\n", 
	     argv[i]);
    else if (reply_code == DBUS_SERVICE_REPLY_ALREADY_OWNER)
      printf("Service %s: %s already owner (shouldn't happen)\n", 
	     argv[i], argv[0]);
    dbus_message_unref (reply);
  }

  /* OK, we've acquired all of the services. 
     Now we watch for incoming messages. */

  dbus_connection_setup_with_g_main (connection, NULL);

  handler = dbus_message_handler_new (handler_func, NULL, NULL);
  dbus_connection_add_filter (connection, handler);

  g_main_loop_run (loop);

  dbus_connection_disconnect (connection);

  exit (0);
}