2004-03-16 Richard Hult <richard@imendio.com>

* bus/activation.c: (bus_activation_service_created),
	(bus_activation_send_pending_auto_activation_messages),
	(bus_activation_activate_service):
	* bus/activation.h:
	* bus/dispatch.c: (bus_dispatch),
	(check_nonexistent_service_auto_activation),
	(check_service_auto_activated),
	(check_segfault_service_auto_activation),
	(check_existent_service_auto_activation), (bus_dispatch_test):
	* bus/driver.c: (bus_driver_handle_activate_service):
	* bus/services.c: (bus_registry_acquire_service):
	* dbus/dbus-message.c: (dbus_message_set_auto_activation),
	(dbus_message_get_auto_activation):
	* dbus/dbus-message.h:
	* dbus/dbus-protocol.h: Implement auto-activation.
This commit is contained in:
Richard Hult 2004-03-16 18:00:35 +00:00
parent 24ffe79c80
commit 93f433a17a
10 changed files with 738 additions and 32 deletions

View file

@ -1,3 +1,23 @@
2004-03-16 Richard Hult <richard@imendio.com>
* bus/activation.c: (bus_activation_service_created),
(bus_activation_send_pending_auto_activation_messages),
(bus_activation_activate_service):
* bus/activation.h:
* bus/dispatch.c: (bus_dispatch),
(check_nonexistent_service_auto_activation),
(check_service_auto_activated),
(check_segfault_service_auto_activation),
(check_existent_service_auto_activation), (bus_dispatch_test):
* bus/driver.c: (bus_driver_handle_activate_service):
* bus/services.c: (bus_registry_acquire_service):
* dbus/dbus-message.c: (dbus_message_set_auto_activation),
(dbus_message_get_auto_activation):
* dbus/dbus-message.h:
* dbus/dbus-protocol.h: Implement auto-activation.
* doc/dbus-specification.xml: Add auto-activation to the spec.
2004-03-12 Olivier Andrieu <oliv__a@users.sourceforge.net>
* dbus/dbus-marshal.c (_dbus_marshal_get_arg_end_pos):

View file

@ -76,6 +76,8 @@ struct BusPendingActivationEntry
{
DBusMessage *activation_message;
DBusConnection *connection;
dbus_bool_t auto_activation;
};
typedef struct
@ -904,30 +906,90 @@ bus_activation_service_created (BusActivation *activation,
if (dbus_connection_get_is_connected (entry->connection))
{
message = dbus_message_new_method_return (entry->activation_message);
if (!message)
/* Only send activation replies to regular activation requests. */
if (!entry->auto_activation)
{
BUS_SET_OOM (error);
goto error;
message = dbus_message_new_method_return (entry->activation_message);
if (!message)
{
BUS_SET_OOM (error);
goto error;
}
if (!dbus_message_append_args (message,
DBUS_TYPE_UINT32, DBUS_ACTIVATION_REPLY_ACTIVATED,
DBUS_TYPE_INVALID))
{
dbus_message_unref (message);
BUS_SET_OOM (error);
goto error;
}
if (!bus_transaction_send_from_driver (transaction, entry->connection, message))
{
dbus_message_unref (message);
BUS_SET_OOM (error);
goto error;
}
dbus_message_unref (message);
}
}
link = next;
}
if (!dbus_message_append_args (message,
DBUS_TYPE_UINT32, DBUS_ACTIVATION_REPLY_ACTIVATED,
DBUS_TYPE_INVALID))
return TRUE;
error:
return FALSE;
}
dbus_bool_t
bus_activation_send_pending_auto_activation_messages (BusActivation *activation,
BusService *service,
BusTransaction *transaction,
DBusError *error)
{
BusPendingActivation *pending_activation;
DBusList *link;
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
/* Check if it's a pending activation */
pending_activation = _dbus_hash_table_lookup_string (activation->pending_activations,
bus_service_get_name (service));
if (!pending_activation)
return TRUE;
link = _dbus_list_get_first_link (&pending_activation->entries);
while (link != NULL)
{
BusPendingActivationEntry *entry = link->data;
DBusList *next = _dbus_list_get_next_link (&pending_activation->entries, link);
if (entry->auto_activation && dbus_connection_get_is_connected (entry->connection))
{
DBusConnection *addressed_recipient;
addressed_recipient = bus_service_get_primary_owner (service);
/* Check the security policy, which has the side-effect of adding an
* expected pending reply.
*/
if (!bus_context_check_security_policy (activation->context, transaction,
entry->connection,
addressed_recipient,
addressed_recipient,
entry->activation_message, error))
goto error;
if (!bus_transaction_send (transaction, addressed_recipient, entry->activation_message))
{
dbus_message_unref (message);
BUS_SET_OOM (error);
goto error;
}
if (!bus_transaction_send_from_driver (transaction, entry->connection, message))
{
dbus_message_unref (message);
BUS_SET_OOM (error);
goto error;
}
dbus_message_unref (message);
}
link = next;
@ -940,7 +1002,7 @@ bus_activation_service_created (BusActivation *activation,
goto error;
}
_dbus_hash_table_remove_string (activation->pending_activations, service_name);
_dbus_hash_table_remove_string (activation->pending_activations, bus_service_get_name (service));
return TRUE;
@ -1225,6 +1287,7 @@ dbus_bool_t
bus_activation_activate_service (BusActivation *activation,
DBusConnection *connection,
BusTransaction *transaction,
dbus_bool_t auto_activation,
DBusMessage *activation_message,
const char *service_name,
DBusError *error)
@ -1300,6 +1363,8 @@ bus_activation_activate_service (BusActivation *activation,
return FALSE;
}
pending_activation_entry->auto_activation = auto_activation;
pending_activation_entry->activation_message = activation_message;
dbus_message_ref (activation_message);
pending_activation_entry->connection = connection;

View file

@ -29,21 +29,28 @@
#include "bus.h"
BusActivation* bus_activation_new (BusContext *context,
const DBusString *address,
DBusList **directories,
DBusError *error);
const DBusString *address,
DBusList **directories,
DBusError *error);
BusActivation* bus_activation_ref (BusActivation *activation);
void bus_activation_unref (BusActivation *activation);
dbus_bool_t bus_activation_activate_service (BusActivation *activation,
DBusConnection *connection,
BusTransaction *transaction,
DBusMessage *activation_message,
const char *service_name,
DBusError *error);
DBusConnection *connection,
BusTransaction *transaction,
dbus_bool_t auto_activation,
DBusMessage *activation_message,
const char *service_name,
DBusError *error);
dbus_bool_t bus_activation_service_created (BusActivation *activation,
const char *service_name,
BusTransaction *transaction,
DBusError *error);
const char *service_name,
BusTransaction *transaction,
DBusError *error);
dbus_bool_t bus_activation_send_pending_auto_activation_messages (BusActivation *activation,
BusService *service,
BusTransaction *transaction,
DBusError *error);
#endif /* BUS_ACTIVATION_H */

View file

@ -3,6 +3,7 @@
*
* Copyright (C) 2003 CodeFactory AB
* Copyright (C) 2003 Red Hat, Inc.
* Copyright (C) 2004 Imendio HB
*
* Licensed under the Academic Free License version 2.0
*
@ -26,6 +27,7 @@
#include "connection.h"
#include "driver.h"
#include "services.h"
#include "activation.h"
#include "utils.h"
#include "bus.h"
#include "signals.h"
@ -257,7 +259,27 @@ bus_dispatch (DBusConnection *connection,
_dbus_string_init_const (&service_string, service_name);
service = bus_registry_lookup (registry, &service_string);
if (service == NULL)
if (service == NULL && dbus_message_get_auto_activation (message))
{
BusActivation *activation;
/* We can't do the security policy check here, since the addressed
* recipient service doesn't exist yet. We do it before sending the
* message after the service has been created.
*/
activation = bus_connection_get_activation (connection);
if (!bus_activation_activate_service (activation, connection, transaction, TRUE,
message, service_name, &error))
{
_DBUS_ASSERT_ERROR_IS_SET (&error);
_dbus_verbose ("bus_activation_activate_service() failed\n");
goto out;
}
goto out;
}
else if (service == NULL)
{
dbus_set_error (&error,
DBUS_ERROR_SERVICE_DOES_NOT_EXIST,
@ -378,6 +400,8 @@ bus_dispatch_remove_connection (DBusConnection *connection)
#ifdef DBUS_BUILD_TESTS
#include <stdio.h>
typedef dbus_bool_t (* Check1Func) (BusContext *context);
typedef dbus_bool_t (* Check2Func) (BusContext *context,
DBusConnection *connection);
@ -1170,6 +1194,104 @@ check_nonexistent_service_activation (BusContext *context,
return retval;
}
/* returns TRUE if the correct thing happens,
* but the correct thing may include OOM errors.
*/
static dbus_bool_t
check_nonexistent_service_auto_activation (BusContext *context,
DBusConnection *connection)
{
DBusMessage *message;
dbus_uint32_t serial;
dbus_bool_t retval;
DBusError error;
dbus_error_init (&error);
message = dbus_message_new_method_call (NONEXISTENT_SERVICE_NAME,
"/org/freedesktop/TestSuite",
"org.freedesktop.TestSuite",
"Echo");
if (message == NULL)
return TRUE;
dbus_message_set_auto_activation (message, TRUE);
if (!dbus_connection_send (connection, message, &serial))
{
dbus_message_unref (message);
return TRUE;
}
dbus_message_unref (message);
message = NULL;
bus_test_run_everything (context);
block_connection_until_message_from_bus (context, connection);
bus_test_run_everything (context);
if (!dbus_connection_get_is_connected (connection))
{
_dbus_verbose ("connection was disconnected\n");
return TRUE;
}
retval = FALSE;
message = pop_message_waiting_for_memory (connection);
if (message == NULL)
{
_dbus_warn ("Did not receive a reply to %s %d on %p\n",
"Echo message (auto activation)", serial, connection);
goto out;
}
verbose_message_received (connection, message);
if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR)
{
if (!dbus_message_has_sender (message, DBUS_SERVICE_ORG_FREEDESKTOP_DBUS))
{
_dbus_warn ("Message has wrong sender %s\n",
dbus_message_get_sender (message) ?
dbus_message_get_sender (message) : "(none)");
goto out;
}
if (dbus_message_is_error (message,
DBUS_ERROR_NO_MEMORY))
{
; /* good, this is a valid response */
}
else if (dbus_message_is_error (message,
DBUS_ERROR_ACTIVATE_SERVICE_NOT_FOUND))
{
; /* good, this is expected also */
}
else
{
warn_unexpected (connection, message, "not this error");
goto out;
}
}
else
{
_dbus_warn ("Did not expect to successfully activate %s\n",
NONEXISTENT_SERVICE_NAME);
goto out;
}
retval = TRUE;
out:
if (message)
dbus_message_unref (message);
return retval;
}
static dbus_bool_t
check_base_service_activated (BusContext *context,
DBusConnection *connection,
@ -1397,6 +1519,94 @@ check_service_activated (BusContext *context,
return retval;
}
static dbus_bool_t
check_service_auto_activated (BusContext *context,
DBusConnection *connection,
const char *activated_name,
const char *base_service_name,
DBusMessage *initial_message)
{
DBusMessage *message;
dbus_bool_t retval;
DBusError error;
retval = FALSE;
dbus_error_init (&error);
message = initial_message;
dbus_message_ref (message);
if (dbus_message_is_signal (message,
DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS,
"ServiceCreated"))
{
char *service_name;
CheckServiceCreatedData scd;
reget_service_name_arg:
if (!dbus_message_get_args (message, &error,
DBUS_TYPE_STRING, &service_name,
DBUS_TYPE_INVALID))
{
if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
{
dbus_error_free (&error);
_dbus_wait_for_memory ();
goto reget_service_name_arg;
}
else
{
_dbus_warn ("Message %s doesn't have a service name: %s\n",
"ServiceCreated",
error.message);
dbus_error_free (&error);
goto out;
}
}
if (strcmp (service_name, activated_name) != 0)
{
_dbus_warn ("Expected to see service %s created, saw %s instead\n",
activated_name, service_name);
dbus_free (service_name);
goto out;
}
scd.skip_connection = connection;
scd.failed = FALSE;
scd.expected_service_name = service_name;
bus_test_clients_foreach (check_service_created_foreach,
&scd);
dbus_free (service_name);
if (scd.failed)
goto out;
/* Note that this differs from regular activation in that we don't get a
* reply to ActivateService here.
*/
dbus_message_unref (message);
message = NULL;
}
else
{
warn_unexpected (connection, message, "ServiceCreated for the activated name");
goto out;
}
retval = TRUE;
out:
if (message)
dbus_message_unref (message);
return retval;
}
static dbus_bool_t
check_service_deactivated (BusContext *context,
DBusConnection *connection,
@ -1974,6 +2184,340 @@ check_segfault_service_activation (BusContext *context,
return retval;
}
/* returns TRUE if the correct thing happens,
* but the correct thing may include OOM errors.
*/
static dbus_bool_t
check_segfault_service_auto_activation (BusContext *context,
DBusConnection *connection)
{
DBusMessage *message;
dbus_uint32_t serial;
dbus_bool_t retval;
DBusError error;
dbus_error_init (&error);
dbus_error_init (&error);
message = dbus_message_new_method_call ("org.freedesktop.DBus.TestSuiteSegfaultService",
"/org/freedesktop/TestSuite",
"org.freedesktop.TestSuite",
"Echo");
if (message == NULL)
return TRUE;
dbus_message_set_auto_activation (message, TRUE);
if (!dbus_connection_send (connection, message, &serial))
{
dbus_message_unref (message);
return TRUE;
}
dbus_message_unref (message);
message = NULL;
bus_test_run_everything (context);
block_connection_until_message_from_bus (context, connection);
bus_test_run_everything (context);
if (!dbus_connection_get_is_connected (connection))
{
_dbus_verbose ("connection was disconnected\n");
return TRUE;
}
retval = FALSE;
message = pop_message_waiting_for_memory (connection);
if (message == NULL)
{
_dbus_warn ("Did not receive a reply to %s %d on %p\n",
"Echo message (auto activation)", serial, connection);
goto out;
}
verbose_message_received (connection, message);
if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR)
{
if (!dbus_message_has_sender (message, DBUS_SERVICE_ORG_FREEDESKTOP_DBUS))
{
_dbus_warn ("Message has wrong sender %s\n",
dbus_message_get_sender (message) ?
dbus_message_get_sender (message) : "(none)");
goto out;
}
if (dbus_message_is_error (message,
DBUS_ERROR_NO_MEMORY))
{
; /* good, this is a valid response */
}
else if (dbus_message_is_error (message,
DBUS_ERROR_SPAWN_CHILD_SIGNALED))
{
; /* good, this is expected also */
}
else
{
warn_unexpected (connection, message, "not this error");
goto out;
}
}
else
{
_dbus_warn ("Did not expect to successfully activate segfault service\n");
goto out;
}
retval = TRUE;
out:
if (message)
dbus_message_unref (message);
return retval;
}
#define TEST_ECHO_MESSAGE "Test echo message"
/* returns TRUE if the correct thing happens,
* but the correct thing may include OOM errors.
*/
static dbus_bool_t
check_existent_service_auto_activation (BusContext *context,
DBusConnection *connection)
{
DBusMessage *message;
dbus_uint32_t serial;
dbus_bool_t retval;
DBusError error;
char *base_service;
base_service = NULL;
dbus_error_init (&error);
message = dbus_message_new_method_call (EXISTENT_SERVICE_NAME,
"/org/freedesktop/TestSuite",
"org.freedesktop.TestSuite",
"Echo");
if (message == NULL)
return TRUE;
dbus_message_set_auto_activation (message, TRUE);
if (!dbus_message_append_args (message,
DBUS_TYPE_STRING, TEST_ECHO_MESSAGE,
DBUS_TYPE_INVALID))
{
dbus_message_unref (message);
return TRUE;
}
if (!dbus_connection_send (connection, message, &serial))
{
dbus_message_unref (message);
return TRUE;
}
dbus_message_unref (message);
message = NULL;
bus_test_run_everything (context);
/* now wait for the message bus to hear back from the activated
* service.
*/
block_connection_until_message_from_bus (context, connection);
bus_test_run_everything (context);
if (!dbus_connection_get_is_connected (connection))
{
_dbus_verbose ("connection was disconnected\n");
return TRUE;
}
retval = FALSE;
/* Should get ServiceCreated for the base service, or an error. */
message = pop_message_waiting_for_memory (connection);
if (message == NULL)
{
_dbus_warn ("Did not receive any messages after auto activation %d on %p\n",
serial, connection);
goto out;
}
verbose_message_received (connection, message);
_dbus_verbose (" (after sending %s)\n", "ActivateService");
if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR)
{
if (!dbus_message_has_sender (message, DBUS_SERVICE_ORG_FREEDESKTOP_DBUS))
{
_dbus_warn ("Message has wrong sender %s\n",
dbus_message_get_sender (message) ?
dbus_message_get_sender (message) : "(none)");
goto out;
}
if (dbus_message_is_error (message, DBUS_ERROR_NO_MEMORY) ||
dbus_message_is_error (message, DBUS_ERROR_SPAWN_CHILD_EXITED) ||
dbus_message_is_error (message, DBUS_ERROR_TIMED_OUT))
{
; /* good, those are expected */
retval = TRUE;
goto out;
}
else
{
_dbus_warn ("Did not expect error %s\n",
dbus_message_get_error_name (message));
goto out;
}
}
else
{
dbus_bool_t got_service_deleted;
dbus_bool_t got_error;
if (!check_base_service_activated (context, connection,
message, &base_service))
goto out;
dbus_message_unref (message);
message = NULL;
/* We may need to block here for the test service to exit or finish up */
block_connection_until_message_from_bus (context, connection);
/* Should get ServiceCreated for the activated service name,
* ServiceDeleted on the base service name, or an error.
*/
message = dbus_connection_borrow_message (connection);
if (message == NULL)
{
_dbus_warn ("Did not receive any messages after base service creation notification\n");
goto out;
}
got_service_deleted = dbus_message_is_signal (message,
DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS,
"ServiceDeleted");
got_error = dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR;
dbus_connection_return_message (connection, message);
message = NULL;
if (got_error)
{
if (!check_got_error (context, connection,
DBUS_ERROR_SPAWN_CHILD_EXITED,
DBUS_ERROR_NO_MEMORY,
NULL))
goto out;
/* A service deleted should be coming along now after this error.
* We can also get the error *after* the service deleted.
*/
got_service_deleted = TRUE;
}
if (got_service_deleted)
{
/* The service started up and got a base address, but then
* failed to register under EXISTENT_SERVICE_NAME
*/
CheckServiceDeletedData csdd;
csdd.expected_service_name = base_service;
csdd.failed = FALSE;
bus_test_clients_foreach (check_service_deleted_foreach,
&csdd);
if (csdd.failed)
goto out;
/* Now we should get an error about the service exiting
* if we didn't get it before.
*/
if (!got_error)
{
block_connection_until_message_from_bus (context, connection);
/* and process everything again */
bus_test_run_everything (context);
if (!check_got_error (context, connection,
DBUS_ERROR_SPAWN_CHILD_EXITED,
NULL))
goto out;
}
}
else
{
message = pop_message_waiting_for_memory (connection);
if (message == NULL)
{
_dbus_warn ("Failed to pop message we just put back! should have been a ServiceCreated\n");
goto out;
}
/* Check that ServiceCreated was correctly received */
if (!check_service_auto_activated (context, connection, EXISTENT_SERVICE_NAME,
base_service, message))
goto out;
dbus_message_unref (message);
message = NULL;
}
/* Note: if this test is run in OOM mode, it will block when the bus
* doesn't send a reply due to OOM.
*/
block_connection_until_message_from_bus (context, connection);
message = pop_message_waiting_for_memory (connection);
if (message == NULL)
{
_dbus_warn ("Failed to pop message! Should have been reply from echo message\n");
goto out;
}
if (dbus_message_get_reply_serial (message) != serial)
{
_dbus_warn ("Wrong reply serial\n");
goto out;
}
dbus_message_unref (message);
message = NULL;
if (!check_send_exit_to_service (context, connection,
EXISTENT_SERVICE_NAME,
base_service))
goto out;
}
retval = TRUE;
out:
if (message)
dbus_message_unref (message);
if (base_service)
dbus_free (base_service);
return retval;
}
typedef struct
{
Check1Func func;
@ -2126,6 +2670,24 @@ bus_dispatch_test (const DBusString *test_data_dir)
check2_try_iterations (context, foo, "existent_service_activation",
check_existent_service_activation);
check2_try_iterations (context, foo, "nonexistent_service_auto_activation",
check_nonexistent_service_auto_activation);
check2_try_iterations (context, foo, "segfault_service_auto_activation",
check_segfault_service_auto_activation);
#if 0
/* Note: need to resolve some issues with the testing code in order to run
* this in oom (handle that we sometimes don't get replies back from the bus
* when oom happens, without blocking the test).
*/
check2_try_iterations (context, foo, "existent_service_auto_activation",
check_existent_service_auto_activation);
#endif
if (!check_existent_service_auto_activation (context, foo))
_dbus_assert_not_reached ("existent service auto activation failed");
_dbus_verbose ("Disconnecting foo, bar, and baz\n");
kill_client_connection_unchecked (foo);

View file

@ -587,7 +587,7 @@ bus_driver_handle_activate_service (DBusConnection *connection,
retval = FALSE;
if (!bus_activation_activate_service (activation, connection, transaction,
if (!bus_activation_activate_service (activation, connection, transaction, FALSE,
message, name, error))
{
_DBUS_ASSERT_ERROR_IS_SET (error);

View file

@ -262,6 +262,7 @@ bus_registry_acquire_service (BusRegistry *registry,
DBusConnection *current_owner;
BusClientPolicy *policy;
BusService *service;
BusActivation *activation;
retval = FALSE;
@ -376,7 +377,11 @@ bus_registry_acquire_service (BusRegistry *registry,
*result = DBUS_SERVICE_REPLY_PRIMARY_OWNER;
}
retval = TRUE;
activation = bus_context_get_activation (registry->context);
retval = bus_activation_send_pending_auto_activation_messages (activation,
service,
transaction,
error);
out:
return retval;

View file

@ -4387,6 +4387,35 @@ dbus_message_get_no_reply (DBusMessage *message)
return (*header & DBUS_HEADER_FLAG_NO_REPLY_EXPECTED) != 0;
}
void
dbus_message_set_auto_activation (DBusMessage *message,
dbus_bool_t auto_activation)
{
char *header;
_dbus_return_if_fail (message != NULL);
_dbus_return_if_fail (!message->locked);
header = _dbus_string_get_data_len (&message->header, FLAGS_OFFSET, 1);
if (auto_activation)
*header |= DBUS_HEADER_FLAG_AUTO_ACTIVATION;
else
*header &= ~DBUS_HEADER_FLAG_AUTO_ACTIVATION;
}
dbus_bool_t
dbus_message_get_auto_activation (DBusMessage *message)
{
const char *header;
_dbus_return_val_if_fail (message != NULL, FALSE);
header = _dbus_string_get_const_data_len (&message->header, FLAGS_OFFSET, 1);
return (*header & DBUS_HEADER_FLAG_AUTO_ACTIVATION) != 0;
}
/**
* Gets the service which originated this message,
* or #NULL if unknown or inapplicable.

View file

@ -119,6 +119,10 @@ dbus_bool_t dbus_message_set_reply_serial (DBusMessage *message,
dbus_uint32_t reply_serial);
dbus_uint32_t dbus_message_get_reply_serial (DBusMessage *message);
void dbus_message_set_auto_activation (DBusMessage *message,
dbus_bool_t auto_activation);
dbus_bool_t dbus_message_get_auto_activation (DBusMessage *message);
dbus_bool_t dbus_message_get_path_decomposed (DBusMessage *message,
char ***path);

View file

@ -72,6 +72,7 @@ extern "C" {
/* Header flags */
#define DBUS_HEADER_FLAG_NO_REPLY_EXPECTED 0x1
#define DBUS_HEADER_FLAG_AUTO_ACTIVATION 0x2
/* Header fields */
#define DBUS_HEADER_FIELD_INVALID 0

View file

@ -249,6 +249,12 @@
optimization. However, it is compliant with this specification
to return the reply despite this flag.</entry>
</row>
<row>
<entry>AUTO_ACTIVATION</entry>
<entry>0x2</entry>
<entry>This message automatically activates the
addressed service before the message is delivered.</entry>
</row>
</tbody>
</tgroup>
</informaltable>
@ -674,6 +680,13 @@
However, it is also acceptable to ignore the NO_REPLY_EXPECTED
flag and reply anyway.
</para>
<para>
If a message has the flag AUTO_ACTIVATION, then the addressed
service will be activated before the message is delivered, if
not already active. The message will be held until the service
is successfully activated or has failed to activate; in case
of failure, an activation error will be returned.
</para>
<sect4 id="message-protocol-types-method-apis">
<title>Mapping method calls to native APIs</title>
<para>