New program for tools: dbus-service.c

amk@amk.ca amk@amk.ca
Sun, 21 Sep 2003 10:50:54 -0400


(I sent this a week ago, but the message never showed up; perhaps it's sitting
in a moderator queue somewhere.)

tools/dbus-send lets you send messages, but there's no program that receives 
messages for a specific service; dbus-monitor prints everything on the bus.

dbus-service, the program below, lets you specify up to 100 service names on
the command line and will then listen to them, printing out any received
messages.  The printout is in enough detail that you could write a script
that parsed dbus-service's output, making it possible to write services in 
languages without a dbus binding.  (I'm evaluating D-BUS for use in a
commercial application, and currently don't have time to get Python bindings
working.)

Let me know if I need to sign a copyright assignment for the code to be
added to the distribution.  Comments on the use of the D-BUS API would also
be helpful; is there anything I'm doing wrong?

--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 & num_services < (sizeof(service_names)/sizeof(char*)); 
      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);
}