[PATCH 3/3] activation: optionally, use systemd for system bus activation
Lennart Poettering
mzqohf at 0pointer.de
Wed Jun 23 09:12:07 PDT 2010
Hooks up service activation with systemd. .service files may choose to
define a systemd service to start, but if they don't it's fine, too.
---
bus/activation.c | 177 +++++++++++++++++++++++++++++++++++++++++++++++++++-
bus/activation.h | 3 +-
bus/bus.c | 13 ++++-
bus/bus.h | 2 +
bus/desktop-file.h | 1 +
bus/driver.c | 8 +++
bus/main.c | 7 ++-
bus/test.c | 2 +-
8 files changed, 208 insertions(+), 5 deletions(-)
diff --git a/bus/activation.c b/bus/activation.c
index 9d454c7..ee5efa8 100644
--- a/bus/activation.c
+++ b/bus/activation.c
@@ -70,6 +70,7 @@ typedef struct
char *name;
char *exec;
char *user;
+ char *systemd_service;
unsigned long mtime;
BusServiceDirectory *s_dir;
char *filename;
@@ -91,6 +92,7 @@ typedef struct
BusActivation *activation;
char *service_name;
char *exec;
+ char *systemd_service;
DBusList *entries;
int n_entries;
DBusBabysitter *babysitter;
@@ -198,6 +200,7 @@ bus_pending_activation_unref (BusPendingActivation *pending_activation)
dbus_free (pending_activation->service_name);
dbus_free (pending_activation->exec);
+ dbus_free (pending_activation->systemd_service);
link = _dbus_list_get_first_link (&pending_activation->entries);
@@ -244,6 +247,7 @@ bus_activation_entry_unref (BusActivationEntry *entry)
dbus_free (entry->exec);
dbus_free (entry->user);
dbus_free (entry->filename);
+ dbus_free (entry->systemd_service);
dbus_free (entry);
}
@@ -255,7 +259,7 @@ update_desktop_file_entry (BusActivation *activation,
BusDesktopFile *desktop_file,
DBusError *error)
{
- char *name, *exec, *user, *exec_tmp;
+ char *name, *exec, *user, *exec_tmp, *systemd_service;
BusActivationEntry *entry;
DBusStat stat_buf;
DBusString file_path;
@@ -268,6 +272,7 @@ update_desktop_file_entry (BusActivation *activation,
user = NULL;
exec_tmp = NULL;
entry = NULL;
+ systemd_service = NULL;
dbus_error_init (&tmp_error);
@@ -328,6 +333,30 @@ update_desktop_file_entry (BusActivation *activation,
}
_DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error);
+ /* systemd service is never required */
+ if (!bus_desktop_file_get_string (desktop_file,
+ DBUS_SERVICE_SECTION,
+ DBUS_SERVICE_SYSTEMD_SERVICE,
+ &systemd_service, &tmp_error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (&tmp_error);
+ /* if we got OOM, then exit */
+ if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
+ {
+ dbus_move_error (&tmp_error, error);
+ goto failed;
+ }
+ else
+ {
+ /* if we have error because we didn't find anything then continue */
+ dbus_error_free (&tmp_error);
+ dbus_free (systemd_service);
+ systemd_service = NULL;
+ }
+ }
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error);
+
entry = _dbus_hash_table_lookup_string (s_dir->entries,
_dbus_string_get_const_data (filename));
@@ -355,6 +384,7 @@ update_desktop_file_entry (BusActivation *activation,
entry->name = name;
entry->exec = exec;
entry->user = user;
+ entry->systemd_service = systemd_service;
entry->refcount = 1;
entry->s_dir = s_dir;
@@ -396,6 +426,8 @@ update_desktop_file_entry (BusActivation *activation,
dbus_free (entry->name);
dbus_free (entry->exec);
dbus_free (entry->user);
+ dbus_free (entry->systemd_service);
+ entry->systemd_service = systemd_service;
entry->name = name;
entry->exec = exec;
entry->user = user;
@@ -423,6 +455,7 @@ failed:
dbus_free (name);
dbus_free (exec_tmp);
dbus_free (user);
+ dbus_free (systemd_service);
_dbus_string_free (&file_path);
if (entry)
@@ -1770,6 +1803,19 @@ bus_activation_activate_service (BusActivation *activation,
return FALSE;
}
+ if (entry->systemd_service)
+ {
+ pending_activation->systemd_service = _dbus_strdup (entry->systemd_service);
+ if (!pending_activation->systemd_service)
+ {
+ _dbus_verbose ("Failed to copy systemd service for pending activation\n");
+ BUS_SET_OOM (error);
+ bus_pending_activation_unref (pending_activation);
+ bus_pending_activation_entry_free (pending_activation_entry);
+ return FALSE;
+ }
+ }
+
pending_activation->timeout =
_dbus_timeout_new (bus_context_get_activation_timeout (activation->context),
pending_activation_timed_out,
@@ -1852,6 +1898,95 @@ bus_activation_activate_service (BusActivation *activation,
if (activated)
return TRUE;
+ if (bus_context_get_systemd_activation (activation->context))
+ {
+ if (strcmp (service_name, "org.freedesktop.systemd1") == 0)
+ /* systemd itself is missing apparently. That can happen
+ only during early startup. Let's just wait until systemd
+ connects to us and do nothing. */
+ return TRUE;
+
+ if (entry->systemd_service)
+ {
+ BusTransaction *activation_transaction;
+ DBusString service_string;
+ BusService *service;
+ BusRegistry *registry;
+
+ /* OK, we have a systemd service configured for this entry,
+ hence let's enqueue an activation request message. This
+ is implemented as a directed signal, not a method call,
+ for three reasons: 1) we don't expect a response on
+ success, where we just expect a name appearing on the
+ bus; 2) at this time the systemd service might not yet
+ have connected, so we wouldn't know the message serial at
+ this point to set up a pending call; 3) it is ugly if the
+ bus suddenly becomes the caller of a remote method. */
+
+ message = dbus_message_new_signal (DBUS_PATH_DBUS,
+ "org.freedesktop.systemd1.Activator",
+ "ActivationRequest");
+ if (!message)
+ {
+ _dbus_verbose ("No memory to create activation message\n");
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!dbus_message_set_sender (message, DBUS_SERVICE_DBUS) ||
+ !dbus_message_set_destination (message, "org.freedesktop.systemd1") ||
+ !dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &entry->systemd_service,
+ DBUS_TYPE_INVALID))
+ {
+ _dbus_verbose ("No memory to set args of activation message\n");
+ dbus_message_unref (message);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ /* Create our transaction */
+ activation_transaction = bus_transaction_new (activation->context);
+ if (activation_transaction == NULL)
+ {
+ _dbus_verbose ("No memory to create activation transaction\n");
+ dbus_message_unref (message);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ /* Check whether systemd is already connected */
+ registry = bus_connection_get_registry (connection);
+ _dbus_string_init_const (&service_string, "org.freedesktop.systemd1");
+ service = bus_registry_lookup (registry, &service_string);
+
+ if (service != NULL)
+ /* Wonderful, systemd is connected, let's just send the msg */
+ retval = bus_dispatch_matches (activation_transaction, NULL, bus_service_get_primary_owners_connection (service),
+ message, error);
+ else
+ /* systemd is not around, let's "activate" it. */
+ retval = bus_activation_activate_service (activation, connection, activation_transaction, TRUE,
+ message, "org.freedesktop.systemd1", error);
+
+ dbus_message_unref (message);
+
+ if (!retval)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ _dbus_verbose ("failed to send activation message: %s\n", error->name);
+ bus_transaction_cancel_and_free (activation_transaction);
+ return FALSE;
+ }
+
+ bus_transaction_execute_and_free (activation_transaction);
+ return TRUE;
+ }
+
+ /* OK, we have no configured systemd service, hence let's
+ proceed with traditional activation. */
+ }
+
/* use command as system and session different */
if (!_dbus_string_init (&command))
{
@@ -2010,6 +2145,46 @@ bus_activation_list_services (BusActivation *activation,
return FALSE;
}
+dbus_bool_t
+dbus_activation_systemd_failure (BusActivation *activation,
+ DBusMessage *message)
+{
+ DBusError error;
+ const char *code, *str, *unit = NULL;
+
+ dbus_error_init(&error);
+
+ /* This is called whenever the systemd activator sent us a
+ response. We'll invalidate all pending activations that match the
+ unit name. */
+
+ if (dbus_message_get_args (message, &error,
+ DBUS_TYPE_STRING, &unit,
+ DBUS_TYPE_STRING, &code,
+ DBUS_TYPE_STRING, &str,
+ DBUS_TYPE_INVALID))
+ dbus_set_error(&error, code, str);
+
+ if (unit)
+ {
+ DBusHashIter iter;
+
+ _dbus_hash_iter_init (activation->pending_activations,
+ &iter);
+
+ while (_dbus_hash_iter_next (&iter))
+ {
+ BusPendingActivation *p = _dbus_hash_iter_get_value (&iter);
+
+ if (p->systemd_service && strcmp (p->systemd_service, unit) == 0)
+ pending_activation_failed(p, &error);
+ }
+ }
+
+ dbus_error_free(&error);
+
+ return TRUE;
+}
#ifdef DBUS_BUILD_TESTS
diff --git a/bus/activation.h b/bus/activation.h
index 03bfed2..97f25b1 100644
--- a/bus/activation.h
+++ b/bus/activation.h
@@ -57,6 +57,8 @@ dbus_bool_t bus_activation_service_created (BusActivation *activation,
dbus_bool_t bus_activation_list_services (BusActivation *registry,
char ***listp,
int *array_len);
+dbus_bool_t dbus_activation_systemd_failure (BusActivation *activation,
+ DBusMessage *message);
dbus_bool_t bus_activation_send_pending_auto_activation_messages (BusActivation *activation,
BusService *service,
@@ -64,5 +66,4 @@ dbus_bool_t bus_activation_send_pending_auto_activation_messages (BusActivati
DBusError *error);
-
#endif /* BUS_ACTIVATION_H */
diff --git a/bus/bus.c b/bus/bus.c
index 8687fb6..1f8994c 100644
--- a/bus/bus.c
+++ b/bus/bus.c
@@ -60,6 +60,7 @@ struct BusContext
unsigned int syslog : 1;
unsigned int keep_umask : 1;
unsigned int allow_anonymous : 1;
+ unsigned int systemd_activation : 1;
};
static dbus_int32_t server_data_slot = -1;
@@ -276,6 +277,7 @@ static dbus_bool_t
process_config_first_time_only (BusContext *context,
BusConfigParser *parser,
const DBusString *address,
+ dbus_bool_t systemd_activation,
DBusError *error)
{
DBusString log_prefix;
@@ -292,6 +294,8 @@ process_config_first_time_only (BusContext *context,
retval = FALSE;
auth_mechanisms = NULL;
+ context->systemd_activation = systemd_activation;
+
/* Check for an existing pid file. Of course this is a race;
* we'd have to use fcntl() locks on the pid file to
* avoid that. But we want to check for the pid file
@@ -652,6 +656,7 @@ bus_context_new (const DBusString *config_file,
DBusPipe *print_addr_pipe,
DBusPipe *print_pid_pipe,
const DBusString *address,
+ dbus_bool_t systemd_activation,
DBusError *error)
{
DBusString log_prefix;
@@ -706,7 +711,7 @@ bus_context_new (const DBusString *config_file,
goto failed;
}
- if (!process_config_first_time_only (context, parser, address, error))
+ if (!process_config_first_time_only (context, parser, address, systemd_activation, error))
{
_DBUS_ASSERT_ERROR_IS_SET (error);
goto failed;
@@ -1084,6 +1089,12 @@ bus_context_get_servicehelper (BusContext *context)
return context->servicehelper;
}
+dbus_bool_t
+bus_context_get_systemd_activation (BusContext *context)
+{
+ return context->systemd_activation;
+}
+
BusRegistry*
bus_context_get_registry (BusContext *context)
{
diff --git a/bus/bus.h b/bus/bus.h
index b5c7c20..ebef17c 100644
--- a/bus/bus.h
+++ b/bus/bus.h
@@ -76,6 +76,7 @@ BusContext* bus_context_new (const DBusStri
DBusPipe *print_addr_pipe,
DBusPipe *print_pid_pipe,
const DBusString *address,
+ dbus_bool_t systemd_activation,
DBusError *error);
dbus_bool_t bus_context_reload_config (BusContext *context,
DBusError *error);
@@ -87,6 +88,7 @@ dbus_bool_t bus_context_get_id (BusContext
const char* bus_context_get_type (BusContext *context);
const char* bus_context_get_address (BusContext *context);
const char* bus_context_get_servicehelper (BusContext *context);
+dbus_bool_t bus_context_get_systemd_activation (BusContext *context);
BusRegistry* bus_context_get_registry (BusContext *context);
BusConnections* bus_context_get_connections (BusContext *context);
BusActivation* bus_context_get_activation (BusContext *context);
diff --git a/bus/desktop-file.h b/bus/desktop-file.h
index 7f43458..58e78e8 100644
--- a/bus/desktop-file.h
+++ b/bus/desktop-file.h
@@ -35,6 +35,7 @@
#define DBUS_SERVICE_EXEC "Exec"
#define DBUS_SERVICE_USER "User"
#define DBUS_SERVICE_GROUP "Group"
+#define DBUS_SERVICE_SYSTEMD_SERVICE "SystemdService"
typedef struct BusDesktopFile BusDesktopFile;
diff --git a/bus/driver.c b/bus/driver.c
index 0dcfdaa..cc8d1f2 100644
--- a/bus/driver.c
+++ b/bus/driver.c
@@ -1930,6 +1930,14 @@ bus_driver_handle_message (DBusConnection *connection,
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ if (dbus_message_is_signal (message, "org.freedesktop.systemd1.Activator", "ActivationFailure"))
+ {
+ BusContext *context;
+
+ context = bus_connection_get_context (connection);
+ return dbus_activation_systemd_failure(bus_context_get_activation(context), message);
+ }
+
if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
{
_dbus_verbose ("Driver got a non-method-call message, ignoring\n");
diff --git a/bus/main.c b/bus/main.c
index 603e1f8..73a2fe4 100644
--- a/bus/main.c
+++ b/bus/main.c
@@ -75,7 +75,7 @@ signal_handler (int sig)
static void
usage (void)
{
- fprintf (stderr, DBUS_DAEMON_NAME " [--version] [--session] [--system] [--config-file=FILE] [--print-address[=DESCRIPTOR]] [--print-pid[=DESCRIPTOR]] [--fork] [--nofork] [--introspect] [--address=ADDRESS]\n");
+ fprintf (stderr, DBUS_DAEMON_NAME " [--version] [--session] [--system] [--config-file=FILE] [--print-address[=DESCRIPTOR]] [--print-pid[=DESCRIPTOR]] [--fork] [--nofork] [--introspect] [--address=ADDRESS] [--systemd-activation]\n");
exit (1);
}
@@ -274,6 +274,7 @@ main (int argc, char **argv)
dbus_bool_t print_pid;
dbus_bool_t is_session_bus;
int force_fork;
+ dbus_bool_t systemd_activation;
if (!_dbus_string_init (&config_file))
return 1;
@@ -291,6 +292,7 @@ main (int argc, char **argv)
print_pid = FALSE;
is_session_bus = FALSE;
force_fork = FORK_FOLLOW_CONFIG_FILE;
+ systemd_activation = FALSE;
prev_arg = NULL;
i = 1;
@@ -310,6 +312,8 @@ main (int argc, char **argv)
force_fork = FORK_NEVER;
else if (strcmp (arg, "--fork") == 0)
force_fork = FORK_ALWAYS;
+ else if (strcmp (arg, "--systemd-activation") == 0)
+ systemd_activation = TRUE;
else if (strcmp (arg, "--system") == 0)
{
check_two_config_files (&config_file, "system");
@@ -488,6 +492,7 @@ main (int argc, char **argv)
context = bus_context_new (&config_file, force_fork,
&print_addr_pipe, &print_pid_pipe,
_dbus_string_get_length(&address) > 0 ? &address : NULL,
+ systemd_activation,
&error);
_dbus_string_free (&config_file);
if (context == NULL)
diff --git a/bus/test.c b/bus/test.c
index 7af143b..6efad6e 100644
--- a/bus/test.c
+++ b/bus/test.c
@@ -323,7 +323,7 @@ bus_context_new_test (const DBusString *test_data_dir,
}
dbus_error_init (&error);
- context = bus_context_new (&config_file, FALSE, NULL, NULL, NULL, &error);
+ context = bus_context_new (&config_file, FALSE, NULL, NULL, NULL, FALSE, &error);
if (context == NULL)
{
_DBUS_ASSERT_ERROR_IS_SET (&error);
--
1.7.0.1
Lennart
--
Lennart Poettering Red Hat, Inc.
lennart [at] poettering [dot] net
http://0pointer.net/lennart/ GnuPG 0x1A015CC4
More information about the dbus
mailing list