dbus/test/test-service.c
Colin Walters b7e77c6b03 Ignore exit code zero from activated services
A variety of system components have migrated from legacy init into DBus
service activation.  Many of these system components "daemonize", which
involves forking.  The DBus activation system treated an exit as an
activation failure, assuming that the child process which grabbed the
DBus name didn't run first.

While we're in here, also differentiate in this code path between the
servicehelper (system) versus direct activation (session) paths.  In
the session activation path our error message mentioned a helper
process which was confusing, since none was involved.

Based on a patch and debugging research from Ray Strode <rstrode@redhat.com>
2010-01-28 17:01:24 -05:00

489 lines
13 KiB
C

#include "test-utils.h"
#include <unistd.h>
static DBusLoop *loop;
static dbus_bool_t already_quit = FALSE;
static dbus_bool_t hello_from_self_reply_received = FALSE;
static void
quit (void)
{
if (!already_quit)
{
_dbus_loop_quit (loop);
already_quit = TRUE;
}
}
static void
die (const char *message)
{
fprintf (stderr, "*** test-service: %s", message);
exit (1);
}
static void
check_hello_from_self_reply (DBusPendingCall *pcall,
void *user_data)
{
DBusMessage *reply;
DBusMessage *echo_message, *echo_reply = NULL;
DBusError error;
DBusConnection *connection;
int type;
dbus_error_init (&error);
connection = dbus_bus_get (DBUS_BUS_STARTER, &error);
if (connection == NULL)
{
fprintf (stderr, "*** Failed to open connection to activating message bus: %s\n",
error.message);
dbus_error_free (&error);
die("no memory");
}
echo_message = (DBusMessage *)user_data;
reply = dbus_pending_call_steal_reply (pcall);
type = dbus_message_get_type (reply);
if (type == DBUS_MESSAGE_TYPE_METHOD_RETURN)
{
const char *s;
printf ("Reply from HelloFromSelf received\n");
if (!dbus_message_get_args (echo_message,
&error,
DBUS_TYPE_STRING, &s,
DBUS_TYPE_INVALID))
{
echo_reply = dbus_message_new_error (echo_message,
error.name,
error.message);
if (echo_reply == NULL)
die ("No memory\n");
}
else
{
echo_reply = dbus_message_new_method_return (echo_message);
if (echo_reply == NULL)
die ("No memory\n");
if (!dbus_message_append_args (echo_reply,
DBUS_TYPE_STRING, &s,
DBUS_TYPE_INVALID))
die ("No memory");
}
if (!dbus_connection_send (connection, echo_reply, NULL))
die ("No memory\n");
dbus_message_unref (echo_reply);
}
else if (type == DBUS_MESSAGE_TYPE_ERROR)
{
dbus_set_error_from_message (&error, reply);
printf ("Error type in reply: %s\n", error.message);
if (strcmp (error.name, DBUS_ERROR_NO_MEMORY) != 0)
{
echo_reply = dbus_message_new_error (echo_reply,
error.name,
error.message);
if (echo_reply == NULL)
die ("No memory\n");
if (!dbus_connection_send (connection, echo_reply, NULL))
die ("No memory\n");
dbus_message_unref (echo_reply);
}
dbus_error_free (&error);
}
else
_dbus_assert_not_reached ("Unexpected message received\n");
hello_from_self_reply_received = TRUE;
dbus_message_unref (reply);
dbus_message_unref (echo_message);
dbus_pending_call_unref (pcall);
dbus_connection_unref (connection);
}
static DBusHandlerResult
handle_run_hello_from_self (DBusConnection *connection,
DBusMessage *message)
{
DBusError error;
DBusMessage *reply, *self_message;
DBusPendingCall *pcall;
char *s;
_dbus_verbose ("sending reply to Echo method\n");
dbus_error_init (&error);
if (!dbus_message_get_args (message,
&error,
DBUS_TYPE_STRING, &s,
DBUS_TYPE_INVALID))
{
reply = dbus_message_new_error (message,
error.name,
error.message);
if (reply == NULL)
die ("No memory\n");
if (!dbus_connection_send (connection, reply, NULL))
die ("No memory\n");
dbus_message_unref (reply);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
printf ("Sending HelloFromSelf\n");
_dbus_verbose ("*** Sending message to self\n");
self_message = dbus_message_new_method_call ("org.freedesktop.DBus.TestSuiteEchoService",
"/org/freedesktop/TestSuite",
"org.freedesktop.TestSuite",
"HelloFromSelf");
if (self_message == NULL)
die ("No memory");
if (!dbus_connection_send_with_reply (connection, self_message, &pcall, -1))
die("No memory");
dbus_message_ref (message);
if (!dbus_pending_call_set_notify (pcall, check_hello_from_self_reply, (void *)message, NULL))
die("No memory");
printf ("Sent HelloFromSelf\n");
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult
handle_echo (DBusConnection *connection,
DBusMessage *message)
{
DBusError error;
DBusMessage *reply;
char *s;
_dbus_verbose ("sending reply to Echo method\n");
dbus_error_init (&error);
if (!dbus_message_get_args (message,
&error,
DBUS_TYPE_STRING, &s,
DBUS_TYPE_INVALID))
{
reply = dbus_message_new_error (message,
error.name,
error.message);
if (reply == NULL)
die ("No memory\n");
if (!dbus_connection_send (connection, reply, NULL))
die ("No memory\n");
dbus_message_unref (reply);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
reply = dbus_message_new_method_return (message);
if (reply == NULL)
die ("No memory\n");
if (!dbus_message_append_args (reply,
DBUS_TYPE_STRING, &s,
DBUS_TYPE_INVALID))
die ("No memory");
if (!dbus_connection_send (connection, reply, NULL))
die ("No memory\n");
fprintf (stderr, "Echo service echoed string: \"%s\"\n", s);
dbus_message_unref (reply);
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult
handle_delay_echo (DBusConnection *connection,
DBusMessage *message)
{
DBusError error;
DBusMessage *reply;
char *s;
_dbus_verbose ("sleeping for a short time\n");
usleep (50000);
_dbus_verbose ("sending reply to DelayEcho method\n");
dbus_error_init (&error);
if (!dbus_message_get_args (message,
&error,
DBUS_TYPE_STRING, &s,
DBUS_TYPE_INVALID))
{
reply = dbus_message_new_error (message,
error.name,
error.message);
if (reply == NULL)
die ("No memory\n");
if (!dbus_connection_send (connection, reply, NULL))
die ("No memory\n");
dbus_message_unref (reply);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
reply = dbus_message_new_method_return (message);
if (reply == NULL)
die ("No memory\n");
if (!dbus_message_append_args (reply,
DBUS_TYPE_STRING, &s,
DBUS_TYPE_INVALID))
die ("No memory");
if (!dbus_connection_send (connection, reply, NULL))
die ("No memory\n");
fprintf (stderr, "DelayEcho service echoed string: \"%s\"\n", s);
dbus_message_unref (reply);
return DBUS_HANDLER_RESULT_HANDLED;
}
static void
path_unregistered_func (DBusConnection *connection,
void *user_data)
{
/* connection was finalized */
}
static DBusHandlerResult
path_message_func (DBusConnection *connection,
DBusMessage *message,
void *user_data)
{
if (dbus_message_is_method_call (message,
"org.freedesktop.TestSuite",
"Echo"))
return handle_echo (connection, message);
else if (dbus_message_is_method_call (message,
"org.freedesktop.TestSuite",
"DelayEcho"))
return handle_delay_echo (connection, message);
else if (dbus_message_is_method_call (message,
"org.freedesktop.TestSuite",
"Exit"))
{
quit ();
return DBUS_HANDLER_RESULT_HANDLED;
}
else if (dbus_message_is_method_call (message,
"org.freedesktop.TestSuite",
"EmitFoo"))
{
/* Emit the Foo signal */
DBusMessage *signal;
double v_DOUBLE;
_dbus_verbose ("emitting signal Foo\n");
signal = dbus_message_new_signal ("/org/freedesktop/TestSuite",
"org.freedesktop.TestSuite",
"Foo");
if (signal == NULL)
die ("No memory\n");
v_DOUBLE = 42.6;
if (!dbus_message_append_args (signal,
DBUS_TYPE_DOUBLE, &v_DOUBLE,
DBUS_TYPE_INVALID))
die ("No memory");
if (!dbus_connection_send (connection, signal, NULL))
die ("No memory\n");
return DBUS_HANDLER_RESULT_HANDLED;
}
else if (dbus_message_is_method_call (message,
"org.freedesktop.TestSuite",
"RunHelloFromSelf"))
{
return handle_run_hello_from_self (connection, message);
}
else if (dbus_message_is_method_call (message,
"org.freedesktop.TestSuite",
"HelloFromSelf"))
{
DBusMessage *reply;
printf ("Received the HelloFromSelf message\n");
reply = dbus_message_new_method_return (message);
if (reply == NULL)
die ("No memory");
if (!dbus_connection_send (connection, reply, NULL))
die ("No memory");
return DBUS_HANDLER_RESULT_HANDLED;
}
else
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
static DBusObjectPathVTable
echo_vtable = {
path_unregistered_func,
path_message_func,
NULL,
};
static const char* echo_path = "/org/freedesktop/TestSuite" ;
static DBusHandlerResult
filter_func (DBusConnection *connection,
DBusMessage *message,
void *user_data)
{
if (dbus_message_is_signal (message,
DBUS_INTERFACE_LOCAL,
"Disconnected"))
{
quit ();
return DBUS_HANDLER_RESULT_HANDLED;
}
else
{
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
}
int
main (int argc,
char **argv)
{
DBusError error;
int result;
DBusConnection *connection;
const char *name;
dbus_bool_t do_fork;
if (argc != 3)
{
name = "org.freedesktop.DBus.TestSuiteEchoService";
do_fork = FALSE;
}
else
{
name = argv[1];
do_fork = strcmp (argv[2], "fork") == 0;
}
/* The bare minimum for simulating a program "daemonizing"; the intent
* is to test services which move from being legacy init scripts to
* activated services.
* https://bugzilla.redhat.com/show_bug.cgi?id=545267
*/
if (do_fork)
{
pid_t pid = fork ();
if (pid != 0)
exit (0);
sleep (1);
}
dbus_error_init (&error);
connection = dbus_bus_get (DBUS_BUS_STARTER, &error);
if (connection == NULL)
{
fprintf (stderr, "*** Failed to open connection to activating message bus: %s\n",
error.message);
dbus_error_free (&error);
return 1;
}
loop = _dbus_loop_new ();
if (loop == NULL)
die ("No memory\n");
if (!test_connection_setup (loop, connection))
die ("No memory\n");
if (!dbus_connection_add_filter (connection,
filter_func, NULL, NULL))
die ("No memory");
if (!dbus_connection_register_object_path (connection,
echo_path,
&echo_vtable,
(void*) 0xdeadbeef))
die ("No memory");
{
void *d;
if (!dbus_connection_get_object_path_data (connection, echo_path, &d))
die ("No memory");
if (d != (void*) 0xdeadbeef)
die ("dbus_connection_get_object_path_data() doesn't seem to work right\n");
}
result = dbus_bus_request_name (connection, name,
0, &error);
if (dbus_error_is_set (&error))
{
fprintf (stderr, "Error %s\n", error.message);
_dbus_verbose ("*** Failed to acquire service: %s\n",
error.message);
dbus_error_free (&error);
exit (1);
}
_dbus_verbose ("*** Test service entering main loop\n");
_dbus_loop_run (loop);
test_connection_shutdown (loop, connection);
dbus_connection_remove_filter (connection, filter_func, NULL);
dbus_connection_unref (connection);
_dbus_loop_unref (loop);
loop = NULL;
dbus_shutdown ();
_dbus_verbose ("*** Test service exiting\n");
return 0;
}