Commit 10f8b67a authored by Alban Crequy's avatar Alban Crequy Committed by Simon McVittie

Add dbus-test-tool, currently with "echo" and "spam" modes

This is installed by default, but easy to filter out for embedded systems
or whatever.

Based on earlier work by Simon McVittie and Will Thompson

Bug: https://bugs.freedesktop.org/show_bug.cgi?id=34140Reviewed-by: default avatarSimon McVittie <simon.mcvittie@collabora.co.uk>
parent c88525f8
......@@ -19,3 +19,4 @@ dbus-glib-bindings.h
run-with-tmp-session-bus.conf
print-introspect
dbus-bus-introspect.xml
dbus-test-tool
......@@ -15,6 +15,7 @@ bin_PROGRAMS = \
dbus-launch \
dbus-monitor \
dbus-send \
dbus-test-tool \
$(NULL)
if DBUS_UNIX
......@@ -81,6 +82,16 @@ dist_examples_SCRIPTS = \
GetAllMatchRules.py \
$(NULL)
dbus_test_tool_SOURCES = \
dbus-echo.c \
dbus-spam.c \
tool-common.c \
tool-common.h \
test-tool.c \
test-tool.h \
$(NULL)
dbus_test_tool_LDADD = $(top_builddir)/dbus/libdbus-1.la
EXTRA_DIST = run-with-tmp-session-bus.sh strtoll.c strtoull.c
CLEANFILES = \
run-with-tmp-session-bus.conf
......
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* dbus-echo.c - a plain libdbus echo server
*
* Copyright © 2003 Philip Blundell <philb@gnu.org>
* Copyright © 2011 Nokia Corporation
* Copyright © 2014 Collabora Ltd.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dbus/dbus.h>
#include "test-tool.h"
#include "tool-common.h"
static void
usage (int exit_with)
{
fprintf (stderr,
"Usage: dbus-test-tool echo [OPTIONS]\n"
"\n"
"Respond to all method calls with an empty reply.\n"
"\n"
"Options:\n"
"\n"
" --name=NAME claim this well-known name first\n"
"\n"
" --sleep=N sleep N milliseconds before sending each reply\n"
"\n"
" --session use the session bus (default)\n"
" --system use the system bus\n"
);
exit (exit_with);
}
static DBusHandlerResult
filter (DBusConnection *connection,
DBusMessage *message,
void *user_data)
{
DBusMessage *reply;
int *sleep_ms = user_data;
if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
if (*sleep_ms > 0)
{
tool_millisleep (*sleep_ms);
}
reply = dbus_message_new_method_return (message);
if (reply == NULL)
tool_oom ("allocating reply");
if (!dbus_connection_send (connection, reply, NULL))
tool_oom ("sending reply");
dbus_message_unref (reply);
return DBUS_HANDLER_RESULT_HANDLED;
}
int
dbus_test_tool_echo (int argc, char **argv)
{
DBusConnection *connection;
DBusError error = DBUS_ERROR_INIT;
DBusBusType type = DBUS_BUS_SESSION;
int i;
int sleep_ms = -1;
const char *name = NULL;
/* argv[1] is the tool name, so start from 2 */
for (i = 2; i < argc; i++)
{
const 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 (strstr (arg, "--name=") == arg)
{
name = arg + strlen ("--name=");
}
else if (strstr (arg, "--sleep-ms=") == arg)
{
sleep_ms = atoi (arg + strlen ("--sleep-ms="));
}
else
{
usage (2);
}
}
connection = dbus_bus_get (type, &error);
if (connection == NULL)
{
fprintf (stderr, "Failed to connect to bus: %s: %s\n",
error.name, error.message);
dbus_error_free (&error);
return 1;
}
if (name != NULL)
{
if (dbus_bus_request_name (connection, name, DBUS_NAME_FLAG_DO_NOT_QUEUE,
NULL) != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
{
fprintf (stderr, "failed to take bus name %s\n", name);
exit (1);
}
}
else
{
printf ("%s\n", dbus_bus_get_unique_name (connection));
}
if (!dbus_connection_add_filter (connection, filter, &sleep_ms, NULL))
tool_oom ("adding message filter");
while (dbus_connection_read_write_dispatch (connection, -1))
{}
dbus_connection_unref (connection);
return 0;
}
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* dbus-spam.c - a plain libdbus message-sender, loosely based on dbus-send
*
* Copyright © 2003 Philip Blundell <philb@gnu.org>
* Copyright © 2011 Nokia Corporation
* Copyright © 2014 Collabora Ltd.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <dbus/dbus.h>
#include "test-tool.h"
#include "tool-common.h"
static dbus_bool_t ignore_errors = FALSE;
static void
usage (int ecode)
{
fprintf (stderr,
"Usage: dbus-test-tool spam [OPTIONS]\n"
"\n"
"Repeatedly call com.example.Spam() on the given D-Bus service.\n"
"\n"
"Options:\n"
"\n"
" --session use the session bus (default)\n"
" --system use the system bus\n"
"\n"
" --ignore-errors ignore errors\n"
" --dest=NAME call methods on NAME (default " DBUS_SERVICE_DBUS ")\n"
"\n"
" --count=N send N messages (default 1)\n"
" --queue=N queue up N messages at a time (default 1)\n"
" --flood send all messages immediately\n"
" --no-reply set the NO_REPLY flag (implies --flood)\n"
"\n"
" --string send payload as a string (default)\n"
" --bytes send payload as a byte-array\n"
" --empty send an empty payload\n"
"\n"
" --payload=S use S as payload (default \"hello, world!\")\n"
" --stdin read payload from stdin, until EOF\n"
" --message-stdin read a complete D-Bus message from stdin\n"
" --random-size read whitespace-separated ASCII decimal\n"
" payload sizes from stdin and pick one randomly\n"
" for each message\n"
"\n"
" --seed=SEED seed for srand (default is time())\n"
"\n"
);
exit (ecode);
}
static void
pc_notify (DBusPendingCall *pc,
void *data)
{
DBusMessage *message;
int *received_p = data;
DBusError error;
dbus_error_init (&error);
message = dbus_pending_call_steal_reply (pc);
if (!ignore_errors && dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR)
{
dbus_set_error_from_message (&error, message);
fprintf (stderr, "Failed to receive reply #%d: %s: %s\n", *received_p,
error.name, error.message);
}
else
{
VERBOSE (stderr, "received message #%d\n", *received_p);
}
(*received_p)++;
}
static void
consume_stdin (char **payload_p,
size_t *len_p)
{
const size_t BLOCK_SIZE = 4096;
size_t len = BLOCK_SIZE;
size_t pos = 0;
size_t n;
char *buf;
buf = dbus_malloc (len);
if (buf == NULL)
tool_oom ("reading payload from stdin");
while (1)
{
if (len - pos < BLOCK_SIZE)
{
char *tmp = dbus_realloc (buf, len + BLOCK_SIZE);
if (tmp == NULL)
tool_oom ("reading payload from stdin");
buf = tmp;
len += BLOCK_SIZE;
}
n = fread (buf + pos, 1, len - pos, stdin);
if (n <= 0)
{
/* EOF or error - treat as EOF */
break;
}
pos += n;
}
*len_p = pos;
*payload_p = buf;
}
int
dbus_test_tool_spam (int argc, char **argv)
{
DBusConnection *connection;
DBusError error = DBUS_ERROR_INIT;
DBusBusType type = DBUS_BUS_SESSION;
const char *destination = DBUS_SERVICE_DBUS;
int i;
int count = 1;
int sent = 0;
int received = 0;
int queue_len = 1;
const char *payload = NULL;
char *payload_buf = NULL;
size_t payload_len;
int payload_type = DBUS_TYPE_STRING;
DBusMessage *template = NULL;
dbus_bool_t no_reply = FALSE;
unsigned int seed = time (NULL);
int n_random_sizes = 0;
unsigned int *random_sizes = NULL;
/* argv[1] is the tool name, so start from 2 */
for (i = 2; i < argc; i++)
{
const 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 (strstr (arg, "--count=") == arg)
{
count = atoi (arg + strlen ("--count="));
if (count < 1)
usage (2);
}
else if (strcmp (arg, "--ignore-errors") == 0)
{
ignore_errors = TRUE;
}
else if (strstr (arg, "--dest=") == arg)
{
destination = arg + strlen ("--dest=");
}
else if (strstr (arg, "--payload=") == arg)
{
payload = arg + strlen ("--payload=");
}
else if (strcmp (arg, "--stdin") == 0)
{
consume_stdin (&payload_buf, &payload_len);
payload = payload_buf;
}
else if (strcmp (arg, "--message-stdin") == 0)
{
consume_stdin (&payload_buf, &payload_len);
payload = payload_buf;
template = dbus_message_demarshal (payload, payload_len, &error);
if (template == NULL)
{
fprintf (stderr, "Unable to demarshal template message: %s: %s",
error.name, error.message);
exit (1);
}
if (dbus_message_get_type (template) != DBUS_MESSAGE_TYPE_METHOD_CALL)
{
fprintf (stderr, "Template message must be a method call\n");
exit (1);
}
}
else if (strcmp (arg, "--random-size") == 0)
{
unsigned int len, max = 0;
int j, consumed = 0;
const char *p;
consume_stdin (&payload_buf, &payload_len);
for (p = payload_buf; p < payload_buf + payload_len; p += consumed)
{
/* the space character matches any (or no) whitespace */
if (sscanf (p, " %u %n", &len, &consumed) == 0)
break;
n_random_sizes++;
}
random_sizes = dbus_new0 (int, n_random_sizes);
if (random_sizes == NULL)
tool_oom ("allocating array of message lengths");
for (p = payload_buf, j = 0;
p < payload_buf + payload_len && j < n_random_sizes;
p += consumed, j++)
{
sscanf (p, " %u %n", &len, &consumed);
random_sizes[j] = len;
if (len > max)
max = len;
}
dbus_free (payload_buf);
payload_len = max + 1;
payload_buf = dbus_new (char, payload_len);
payload = payload_buf;
if (payload_buf == NULL)
tool_oom ("allocating maximum-sized payload");
memset (payload_buf, 'X', payload_len);
payload_buf[payload_len - 1] = '\0';
}
else if (strcmp (arg, "--empty") == 0)
{
payload_type = DBUS_TYPE_INVALID;
}
else if (strcmp (arg, "--string") == 0)
{
payload_type = DBUS_TYPE_STRING;
}
else if (strcmp (arg, "--bytes") == 0)
{
payload_type = DBUS_TYPE_ARRAY;
}
else if (strcmp (arg, "--flood") == 0)
{
queue_len = -1;
}
else if (strcmp (arg, "--no-reply") == 0)
{
queue_len = -1;
no_reply = TRUE;
}
else if (strstr (arg, "--queue=") == arg)
{
queue_len = atoi (arg + strlen ("--queue="));
if (queue_len < 1)
usage (2);
}
else if (strstr (arg, "--seed=") == arg)
{
seed = strtoul (arg + strlen ("--seed="), NULL, 10);
}
else
{
usage (2);
}
}
srand (seed);
if (payload == NULL)
{
payload = "hello, world!";
payload_len = strlen (payload);
}
VERBOSE (stderr, "Will send up to %d messages, with up to %d queued\n",
count, queue_len);
connection = dbus_bus_get_private (type, &error);
if (connection == NULL)
{
fprintf (stderr, "Failed to connect to bus: %s: %s\n",
error.name, error.message);
dbus_error_free (&error);
return 1;
}
while (no_reply ? sent < count : received < count)
{
while (sent < count &&
(queue_len == -1 || sent < queue_len + received))
{
DBusMessage *message;
if (template != NULL)
{
message = dbus_message_copy (template);
if (message == NULL)
tool_oom ("copying message");
dbus_message_set_no_reply (message, no_reply);
}
else
{
dbus_bool_t mem;
unsigned int len = 0;
message = dbus_message_new_method_call (destination,
"/",
"com.example",
"Spam");
if (message == NULL)
tool_oom ("allocating message");
dbus_message_set_no_reply (message, no_reply);
switch (payload_type)
{
case DBUS_TYPE_STRING:
if (random_sizes != NULL)
{
/* this isn't fair, strictly speaking - the first few
* are a bit more likely to be chosen, unless
* RAND_MAX is divisible by n_random_sizes - but it's
* good enough for traffic-generation */
len = random_sizes[rand () % n_random_sizes];
payload_buf[len] = '\0';
}
mem = dbus_message_append_args (message,
DBUS_TYPE_STRING, &payload,
DBUS_TYPE_INVALID);
if (random_sizes != NULL)
{
/* undo the truncation above */
payload_buf[len] = 'X';
}
break;
case DBUS_TYPE_ARRAY:
len = payload_len;
/* as above, not strictly fair, but close enough */
if (random_sizes != NULL)
len = random_sizes[rand () % n_random_sizes];
mem = dbus_message_append_args (message,
DBUS_TYPE_ARRAY,
DBUS_TYPE_BYTE,
&payload,
(dbus_uint32_t) len,
DBUS_TYPE_INVALID);
break;
default:
mem = TRUE;
}
if (!mem)
tool_oom ("building message");
}
if (no_reply)
{
if (!dbus_connection_send (connection, message, NULL))
tool_oom ("sending message");
sent++;
}
else
{
DBusPendingCall *pc;
if (!dbus_connection_send_with_reply (connection,
message,
&pc,
DBUS_TIMEOUT_INFINITE))
tool_oom ("sending message");
VERBOSE (stderr, "sent message #%d\n", sent);
sent++;
if (pc == NULL)
tool_oom ("sending message");
if (dbus_pending_call_get_completed (pc))
pc_notify (pc, &received);
else if (!dbus_pending_call_set_notify (pc, pc_notify, &received,
NULL))
tool_oom ("setting pending call notifier");
dbus_pending_call_unref (pc);
}
dbus_message_unref (message);
}
if (!dbus_connection_read_write_dispatch (connection, -1))
{
fprintf (stderr, "Disconnected from bus\n");
exit (1);
}
}
dbus_connection_flush (connection);
VERBOSE (stderr, "Done\n");
dbus_free (payload_buf);
if (template != NULL)
dbus_message_unref (template);
dbus_connection_close (connection);
dbus_connection_unref (connection);
dbus_shutdown ();
return 0;
}
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* dbus-test-tool - D-Bus swiss army knife
*
* Copyright © 2003 Philip Blundell <philb@gnu.org>
* Copyright © 2011 Nokia Corporation
* Copyright © 2014 Collabora Ltd.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include <config.h>
#include "test-tool.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dbus/dbus.h>
static struct {
const char *name;
int (*callback) (int, char **);
} subcommands[] = {
{ "echo", dbus_test_tool_echo },
{ "spam", dbus_test_tool_spam },
{ NULL, NULL }
};
static void usage (int exit_with) _DBUS_GNUC_NORETURN;
static void
usage (int exit_with)
{
int i;
fprintf (stderr,
"Usage: dbus-test-tool SUBCOMMAND [OPTIONS]\n"
"\n"
"Known SUBCOMMANDs are:\n"
"\n"
);
for (i = 0; subcommands[i].name != NULL; i++)
{
fprintf (stderr, "- %s\n", subcommands[i].name);
}
fprintf (stderr,
"\n"
"For more information: dbus-test-tool SUBCOMMAND --help\n"
);
exit (exit_with);
}
int
main (int argc, char **argv)
{
int i;
if (argc < 2)
{
usage (2);
}
for (i = 0; subcommands[i].name != NULL; i++)
{
if (!strcmp (argv[1], subcommands[i].name))
return subcommands[i].callback (argc, argv);
}
usage (2);
return 2;
}
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* dbus-test-tool - D-Bus swiss army knife
*
* Copyright © 2003 Philip Blundell <philb@gnu.org>
* Copyright © 2011 Nokia Corporation