summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/app_info.c22
-rw-r--r--src/app_info.h4
-rw-r--r--src/app_launcher.c58
-rw-r--r--src/app_launcher.h4
-rw-r--r--src/dbus_activation_manager.c257
-rw-r--r--src/dbus_activation_manager.h42
-rw-r--r--src/main.c53
-rw-r--r--src/meson.build3
-rw-r--r--src/systemd_manager.c247
-rw-r--r--src/systemd_manager.h38
10 files changed, 393 insertions, 335 deletions
diff --git a/src/app_info.c b/src/app_info.c
index 03e1f02..0d25d37 100644
--- a/src/app_info.c
+++ b/src/app_info.c
@@ -17,7 +17,6 @@
#include <gio/gio.h>
#include "app_info.h"
-#include "dbus_activation_manager.h"
struct _AppInfo {
GObject parent_instance;
@@ -26,14 +25,14 @@ struct _AppInfo {
gchar *name;
gchar *icon_path;
gchar *command;
- gboolean dbus_activated;
+ gboolean systemd_activated;
gboolean graphical;
AppStatus status;
/*
* `runtime_data` is an opaque pointer depending on the app startup method.
- * It is set in by ProcessManager or DBusActivationManager.
+ * It is set in by ProcessManager or SystemdManager.
*/
gpointer runtime_data;
};
@@ -52,13 +51,7 @@ static void app_info_dispose(GObject *object)
g_clear_pointer(&self->name, g_free);
g_clear_pointer(&self->icon_path, g_free);
g_clear_pointer(&self->app_id, g_free);
-
- if (self->dbus_activated) {
- g_clear_pointer(&self->runtime_data,
- dbus_activation_manager_free_runtime_data);
- } else {
- g_clear_pointer(&self->runtime_data, g_free);
- }
+ g_clear_pointer(&self->runtime_data, g_free);
G_OBJECT_CLASS(app_info_parent_class)->dispose(object);
}
@@ -86,7 +79,8 @@ static void app_info_init(AppInfo *self)
AppInfo *app_info_new(const gchar *app_id, const gchar *name,
const gchar *icon_path, const gchar *command,
- gboolean dbus_activated, gboolean graphical)
+ gboolean systemd_activated,
+ gboolean graphical)
{
AppInfo *self = g_object_new(APPLAUNCHD_TYPE_APP_INFO, NULL);
@@ -94,7 +88,7 @@ AppInfo *app_info_new(const gchar *app_id, const gchar *name,
self->name = g_strdup(name);
self->icon_path = g_strdup(icon_path);
self->command = g_strdup(command);
- self->dbus_activated = dbus_activated;
+ self->systemd_activated = systemd_activated;
self->graphical = graphical;
return self;
@@ -128,11 +122,11 @@ const gchar *app_info_get_command(AppInfo *self)
return self->command;
}
-gboolean app_info_get_dbus_activated(AppInfo *self)
+gboolean app_info_get_systemd_activated(AppInfo *self)
{
g_return_val_if_fail(APPLAUNCHD_IS_APP_INFO(self), FALSE);
- return self->dbus_activated;
+ return self->systemd_activated;
}
gboolean app_info_get_graphical(AppInfo *self)
diff --git a/src/app_info.h b/src/app_info.h
index dd312c4..5e69dc3 100644
--- a/src/app_info.h
+++ b/src/app_info.h
@@ -34,14 +34,14 @@ G_DECLARE_FINAL_TYPE(AppInfo, app_info, APPLAUNCHD,
AppInfo *app_info_new(const gchar *app_id, const gchar *name,
const gchar *icon_path, const gchar *command,
- gboolean dbus_activated, gboolean graphical);
+ gboolean systemd_activated, gboolean graphical);
/* Accessors for read-only members */
const gchar *app_info_get_app_id(AppInfo *self);
const gchar *app_info_get_name(AppInfo *self);
const gchar *app_info_get_icon_path(AppInfo *self);
const gchar *app_info_get_command(AppInfo *self);
-gboolean app_info_get_dbus_activated(AppInfo *self);
+gboolean app_info_get_systemd_activated(AppInfo *self);
gboolean app_info_get_graphical(AppInfo *self);
/* Accessors for read-write members */
diff --git a/src/app_launcher.c b/src/app_launcher.c
index 5c9bddb..423bf19 100644
--- a/src/app_launcher.c
+++ b/src/app_launcher.c
@@ -18,19 +18,27 @@
#include "app_info.h"
#include "app_launcher.h"
-#include "dbus_activation_manager.h"
#include "process_manager.h"
+#include "systemd_manager.h"
#include "utils.h"
typedef struct _AppLauncher {
applaunchdAppLaunchSkeleton parent;
- DBusActivationManager *dbus_manager;
+/* TODO: try to move event and bus properties down to systemd_manager */
+ sd_event *event;
+ sd_bus *bus;
+
ProcessManager *process_manager;
+ SystemdManager *systemd_manager;
GList *apps_list;
} AppLauncher;
+extern GMainLoop *main_loop;
+
+extern GSource *g_sd_event_create_source(sd_event *event, sd_bus *bus);
+
static void app_launcher_iface_init(applaunchdAppLaunchIface *iface);
G_DEFINE_TYPE_WITH_CODE(AppLauncher, app_launcher,
@@ -70,7 +78,7 @@ static void app_launcher_update_applications_list(AppLauncher *self)
g_autofree const gchar *app_id = NULL;
g_autofree const gchar *icon_path = NULL;
AppInfo *app_info = NULL;
- gboolean dbus_activated, graphical;
+ gboolean systemd_activated, graphical;
if (!desktop_info) {
g_warning("Unable to find .desktop file for application '%s'", desktop_id);
@@ -109,13 +117,14 @@ static void app_launcher_update_applications_list(AppLauncher *self)
* An application can be D-Bus activated if one of those conditions are met:
* - its .desktop file contains a "DBusActivatable=true" line
* - it provides a corresponding D-Bus service file
+ * HACK: Use "DBusActivatable=true" in .desktop to mark systemd-based service
*/
/* Default to non-DBus-activatable */
- dbus_activated = FALSE;
+ systemd_activated = FALSE;
if (g_desktop_app_info_get_boolean(desktop_info,
G_KEY_FILE_DESKTOP_KEY_DBUS_ACTIVATABLE)) {
- dbus_activated = TRUE;
+ systemd_activated = TRUE;
} else if (dirlist) {
const gchar *desktop_filename = g_desktop_app_info_get_filename(desktop_info);
g_autofree gchar *service_file = g_strconcat(app_id, ".service", NULL);
@@ -130,7 +139,7 @@ static void app_launcher_update_applications_list(AppLauncher *self)
service_path = g_build_filename(*xdg_data_dir, "dbus-1", "services",
service_file, NULL);
if (g_file_test(service_path, G_FILE_TEST_EXISTS)) {
- dbus_activated = TRUE;
+ systemd_activated = TRUE;
break;
}
}
@@ -149,8 +158,8 @@ static void app_launcher_update_applications_list(AppLauncher *self)
app_info = app_info_new(app_id, g_app_info_get_name(appinfo),
icon_path ? icon_path : "",
- dbus_activated ? "" : g_app_info_get_commandline(appinfo),
- dbus_activated, graphical);
+ g_app_info_get_commandline(appinfo),
+ systemd_activated, graphical);
g_debug("Adding application '%s'", app_id);
@@ -217,13 +226,11 @@ static gboolean app_launcher_start_app(AppLauncher *self, AppInfo *app_info)
* and notify subscribers it should be activated/brought to the
* foreground
*/
- if (app_info_get_dbus_activated(app_info))
- dbus_activation_manager_activate_app(self->dbus_manager, app_info);
app_launcher_started_cb(self, app_id, NULL);
return TRUE;
case APP_STATUS_INACTIVE:
- if (app_info_get_dbus_activated(app_info))
- dbus_activation_manager_start_app(self->dbus_manager, app_info);
+ if (app_info_get_systemd_activated(app_info))
+ systemd_manager_start_app(self->systemd_manager, app_info);
else
process_manager_start_app(self->process_manager, app_info);
return TRUE;
@@ -336,8 +343,8 @@ static void app_launcher_dispose(GObject *object)
if (self->apps_list)
g_list_free_full(g_steal_pointer(&self->apps_list), g_object_unref);
- g_clear_object(&self->dbus_manager);
g_clear_object(&self->process_manager);
+ g_clear_object(&self->systemd_manager);
G_OBJECT_CLASS(app_launcher_parent_class)->dispose(object);
}
@@ -362,6 +369,11 @@ static void app_launcher_iface_init(applaunchdAppLaunchIface *iface)
static void app_launcher_init (AppLauncher *self)
{
+ sd_bus_open_system(&self->bus);
+ sd_event_default(&self->event);
+ sd_bus_attach_event(self->bus, self->event, SD_EVENT_PRIORITY_NORMAL);
+ g_source_attach(g_sd_event_create_source(self->event, self->bus), g_main_loop_get_context(main_loop));
+
/*
* Create the process manager and connect to its signals
* so we get notified on app startup/termination
@@ -374,14 +386,14 @@ static void app_launcher_init (AppLauncher *self)
G_CALLBACK(app_launcher_terminated_cb), self);
/*
- * Create the D-Bus activation manager and connect to its signals
+ * Create the systemd manager and connect to its signals
* so we get notified on app startup/termination
*/
- self->dbus_manager = g_object_new(APPLAUNCHD_TYPE_DBUS_ACTIVATION_MANAGER,
- NULL);
- g_signal_connect_swapped(self->dbus_manager, "started",
+ self->systemd_manager = g_object_new(APPLAUNCHD_TYPE_SYSTEMD_MANAGER,
+ NULL);
+ g_signal_connect_swapped(self->systemd_manager, "started",
G_CALLBACK(app_launcher_started_cb), self);
- g_signal_connect_swapped(self->dbus_manager, "terminated",
+ g_signal_connect_swapped(self->systemd_manager, "terminated",
G_CALLBACK(app_launcher_terminated_cb), self);
/* Initialize the applications list */
@@ -430,3 +442,13 @@ AppInfo *app_launcher_get_app_info(AppLauncher *self, const gchar *app_id)
return NULL;
}
+
+sd_bus *app_launcher_get_bus(AppLauncher *self)
+{
+ return self->bus;
+}
+
+sd_event *app_launcher_get_event(AppLauncher *self)
+{
+ return self->event;
+}
diff --git a/src/app_launcher.h b/src/app_launcher.h
index 789f091..bfbe281 100644
--- a/src/app_launcher.h
+++ b/src/app_launcher.h
@@ -18,6 +18,8 @@
#define APPLAUNCHER_H
#include <glib-object.h>
+#include <systemd/sd-bus.h>
+#include <systemd/sd-event.h>
#include "applaunch-dbus.h"
#include "app_info.h"
@@ -32,6 +34,8 @@ G_DECLARE_FINAL_TYPE(AppLauncher, app_launcher, APPLAUNCHD, APP_LAUNCHER,
AppLauncher *app_launcher_get_default(void);
AppInfo *app_launcher_get_app_info(AppLauncher *self, const gchar *app_id);
+sd_bus *app_launcher_get_bus(AppLauncher *self);
+sd_event *app_launcher_get_event(AppLauncher *self);
G_END_DECLS
diff --git a/src/dbus_activation_manager.c b/src/dbus_activation_manager.c
deleted file mode 100644
index 8c65485..0000000
--- a/src/dbus_activation_manager.c
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * Copyright (C) 2021 Collabora Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gio/gio.h>
-
-#include "app_launcher.h"
-#include "dbus_activation_manager.h"
-#include "fdo-dbus.h"
-
-struct _DBusActivationManager {
- GObject parent_instance;
-};
-
-G_DEFINE_TYPE(DBusActivationManager, dbus_activation_manager, G_TYPE_OBJECT);
-
-enum {
- STARTED,
- TERMINATED,
- N_SIGNALS
-};
-static guint signals[N_SIGNALS];
-
-/*
- * Application info structure, used for storing relevant data
- * in the `starting_apps` and `running_apps` lists
- */
-struct dbus_runtime_data {
- guint watcher;
- fdoApplication *fdo_proxy;
-};
-
-/*
- * Initialization & cleanup functions
- */
-
-static void dbus_activation_manager_dispose(GObject *object)
-{
- G_OBJECT_CLASS(dbus_activation_manager_parent_class)->dispose(object);
-}
-
-static void dbus_activation_manager_finalize(GObject *object)
-{
- G_OBJECT_CLASS(dbus_activation_manager_parent_class)->finalize(object);
-}
-
-static void dbus_activation_manager_class_init(DBusActivationManagerClass *klass)
-{
- GObjectClass *object_class = (GObjectClass *)klass;
-
- object_class->dispose = dbus_activation_manager_dispose;
- object_class->finalize = dbus_activation_manager_finalize;
-
- signals[STARTED] = g_signal_new("started", G_TYPE_FROM_CLASS(klass),
- G_SIGNAL_RUN_LAST, 0 ,
- NULL, NULL, NULL, G_TYPE_NONE,
- 1, G_TYPE_STRING);
-
- signals[TERMINATED] = g_signal_new("terminated", G_TYPE_FROM_CLASS(klass),
- G_SIGNAL_RUN_LAST, 0,
- NULL, NULL, NULL, G_TYPE_NONE,
- 1, G_TYPE_STRING);
-}
-
-static void dbus_activation_manager_init(DBusActivationManager *self)
-{
-}
-
-/*
- * Internal callbacks
- */
-
-/*
- * This function is called when a D-Bus name we're watching just vanished
- * from the session bus. This indicates the underlying application terminated,
- * so we must remove it from the list of running apps and notify listeners
- * of the app termination so they can act accordingly.
- */
-static void dbus_activation_manager_app_terminated_cb(GDBusConnection *connection,
- const gchar *name,
- gpointer data)
-{
- DBusActivationManager *self = data;
- AppLauncher *app_launcher = app_launcher_get_default();
- AppInfo *app_info;
- struct dbus_runtime_data *runtime_data;
-
- g_return_if_fail(APPLAUNCHD_IS_DBUS_ACTIVATION_MANAGER(self));
- g_return_if_fail(APPLAUNCHD_IS_APP_LAUNCHER(app_launcher));
-
- app_info = app_launcher_get_app_info(app_launcher, name);
- g_return_if_fail(APPLAUNCHD_IS_APP_INFO(app_info));
-
- runtime_data = app_info_get_runtime_data(app_info);
- g_debug("Application '%s' vanished from D-Bus", name);
-
- app_info_set_status(app_info, APP_STATUS_INACTIVE);
- app_info_set_runtime_data(app_info, NULL);
-
- dbus_activation_manager_free_runtime_data(runtime_data);
-
- g_signal_emit(self, signals[TERMINATED], 0, name);
-}
-
-/*
- * Function called when the name appeared on D-Bus, meaning the application
- * successfully started and registered its D-Bus service.
- */
-static void dbus_activation_manager_app_started_cb(GDBusConnection *connection,
- const gchar *name,
- const gchar *name_owner,
- gpointer data)
-{
- DBusActivationManager *self = data;
- AppLauncher *app_launcher = app_launcher_get_default();
- AppInfo *app_info;
- struct dbus_runtime_data *runtime_data;
-
- g_return_if_fail(APPLAUNCHD_IS_DBUS_ACTIVATION_MANAGER(self));
- g_return_if_fail(APPLAUNCHD_IS_APP_LAUNCHER(app_launcher));
-
- app_info = app_launcher_get_app_info(app_launcher, name);
- g_return_if_fail(APPLAUNCHD_IS_APP_INFO(app_info));
-
- runtime_data = app_info_get_runtime_data(app_info);
-
- g_debug("Application '%s' appeared on D-Bus", name);
- app_info_set_status(app_info, APP_STATUS_RUNNING);
- dbus_activation_manager_activate_app(self, app_info);
- g_signal_emit(self, signals[STARTED], 0, app_info_get_app_id(app_info));
-}
-
-/*
- * Public functions
- */
-
-DBusActivationManager *dbus_activation_manager_new(void)
-{
- return g_object_new(APPLAUNCHD_TYPE_DBUS_ACTIVATION_MANAGER, NULL);
-}
-
-/*
- * Start an application using D-Bus activation.
- */
-gboolean dbus_activation_manager_start_app(DBusActivationManager *self,
- AppInfo *app_info)
-{
- g_return_val_if_fail(APPLAUNCHD_IS_DBUS_ACTIVATION_MANAGER(self), FALSE);
- g_return_val_if_fail(APPLAUNCHD_IS_APP_INFO(app_info), FALSE);
-
- g_autoptr(GError) error = NULL;
- const gchar *app_id = app_info_get_app_id(app_info);
- struct dbus_runtime_data *runtime_data = g_new0(struct dbus_runtime_data, 1);
-
- if (!runtime_data) {
- g_critical("Unable to allocate runtime data structure for '%s'",
- app_id);
- return FALSE;
- }
-
- /*
- * g_bus_watch_name() requests D-Bus activation (due to
- * G_BUS_NAME_WATCHER_FLAGS_AUTO_START) and subscribes to name
- * owner changes so we get notified when the requested application
- * appears and vanishes from D-Bus.
- */
- runtime_data->watcher = g_bus_watch_name(G_BUS_TYPE_SESSION, app_id,
- G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
- dbus_activation_manager_app_started_cb,
- dbus_activation_manager_app_terminated_cb,
- self, NULL);
- if (runtime_data->watcher == 0) {
- g_critical("Unable to request D-Bus activation for '%s'", app_id);
- dbus_activation_manager_free_runtime_data(runtime_data);
- return FALSE;
- }
-
- /* Update application status */
- app_info_set_status(app_info, APP_STATUS_STARTING);
- app_info_set_runtime_data(app_info, runtime_data);
-
- return TRUE;
-}
-
-/*
- * Once an application has been started through D-Bus, we must activate it
- * so it shows its main window, if any.
- * This function doesn't raise an error as headless applications will likely
- * not implement the org.freedesktop.Application interface.
- */
-gboolean dbus_activation_manager_activate_app(DBusActivationManager *self,
- AppInfo *app)
-{
- g_return_val_if_fail(APPLAUNCHD_IS_DBUS_ACTIVATION_MANAGER(self), FALSE);
- g_return_val_if_fail(APPLAUNCHD_IS_APP_INFO(app), FALSE);
-
- g_autoptr(GError) error = NULL;
- struct dbus_runtime_data *runtime_data = app_info_get_runtime_data(app);
- const gchar *app_id = app_info_get_app_id(app);
-
- g_return_val_if_fail(runtime_data != NULL, FALSE);
-
- /* Base object for interface "org.example.Iface" is "/org/example/Iface" */
- g_autofree gchar *path = g_strconcat("/", app_id, NULL);
- g_strdelimit(path, ".", '/');
-
- if (!runtime_data->fdo_proxy) {
- runtime_data->fdo_proxy =
- fdo_application_proxy_new_for_bus_sync(G_BUS_TYPE_SESSION,
- G_DBUS_PROXY_FLAGS_NONE,
- app_id, path, NULL,
- &error);
- }
-
- if (runtime_data->fdo_proxy) {
- GVariantDict dict;
-
- g_variant_dict_init(&dict, NULL);
-
- fdo_application_call_activate_sync (runtime_data->fdo_proxy,
- g_variant_dict_end(&dict),
- NULL, &error);
- if (error) {
- g_warning("Error activating application %s: %s", app_id,
- error->message);
- }
- } else if (error) {
- g_warning("Error creating D-Bus proxy for %s: %s", app_id,
- error->message);
- }
-
- return TRUE;
-}
-
-void dbus_activation_manager_free_runtime_data(gpointer data)
-{
- struct dbus_runtime_data *runtime_data = data;
-
- g_return_if_fail(runtime_data != NULL);
-
- g_clear_object(&runtime_data->fdo_proxy);
- if (runtime_data->watcher > 0)
- g_bus_unwatch_name(runtime_data->watcher);
- g_free(runtime_data);
-}
diff --git a/src/dbus_activation_manager.h b/src/dbus_activation_manager.h
deleted file mode 100644
index e475da4..0000000
--- a/src/dbus_activation_manager.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2021 Collabora Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef DBUSACTIVATIONMANAGER_H
-#define DBUSACTIVATIONMANAGER_H
-
-#include <glib-object.h>
-
-#include "app_info.h"
-
-G_BEGIN_DECLS
-
-#define APPLAUNCHD_TYPE_DBUS_ACTIVATION_MANAGER dbus_activation_manager_get_type()
-
-G_DECLARE_FINAL_TYPE(DBusActivationManager, dbus_activation_manager,
- APPLAUNCHD, DBUS_ACTIVATION_MANAGER, GObject);
-
-DBusActivationManager *dbus_activation_manager_new(void);
-
-gboolean dbus_activation_manager_start_app(DBusActivationManager *self,
- AppInfo *app_info);
-gboolean dbus_activation_manager_activate_app(DBusActivationManager *self,
- AppInfo *app_info);
-
-void dbus_activation_manager_free_runtime_data(gpointer data);
-
-G_END_DECLS
-
-#endif
diff --git a/src/main.c b/src/main.c
index d19038a..2f0e4ef 100644
--- a/src/main.c
+++ b/src/main.c
@@ -16,6 +16,8 @@
#include <glib.h>
#include <glib-unix.h>
+#include <systemd/sd-bus.h>
+#include <systemd/sd-event.h>
#include "app_launcher.h"
#include "applaunch-dbus.h"
@@ -23,7 +25,16 @@
#define APPLAUNCH_DBUS_NAME "org.automotivelinux.AppLaunch"
#define APPLAUNCH_DBUS_PATH "/org/automotivelinux/AppLaunch"
-static GMainLoop *main_loop = NULL;
+/* TODO: see if it makes sense to move systemd event handling specifics
+ and interacting with GLib main loop into systemd_manager.c */
+typedef struct SDEventSource {
+ GSource source;
+ GPollFD pollfd;
+ sd_event *event;
+ sd_bus *bus;
+} SDEventSource;
+
+GMainLoop *main_loop = NULL;
static gboolean quit_cb(gpointer user_data)
{
@@ -60,6 +71,45 @@ static void name_lost_cb(GDBusConnection *connection, const gchar *name,
g_main_loop_quit(main_loop);
}
+static gboolean event_prepare(GSource *source, gint *timeout_) {
+ return sd_event_prepare(((SDEventSource *)source)->event) > 0;
+}
+
+static gboolean event_check(GSource *source) {
+ return sd_event_wait(((SDEventSource *)source)->event, 0) > 0;
+}
+
+static gboolean event_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) {
+ return sd_event_dispatch(((SDEventSource *)source)->event) > 0;
+}
+
+static void event_finalize(GSource *source) {
+ sd_event_unref(((SDEventSource *)source)->event);
+}
+
+static GSourceFuncs event_funcs = {
+ .prepare = event_prepare,
+ .check = event_check,
+ .dispatch = event_dispatch,
+ .finalize = event_finalize,
+};
+
+GSource *g_sd_event_create_source(sd_event *event, sd_bus *bus)
+{
+ SDEventSource *source;
+
+ source = (SDEventSource *)g_source_new(&event_funcs, sizeof(SDEventSource));
+
+ source->event = sd_event_ref(event);
+ source->bus = sd_bus_ref(bus);
+ source->pollfd.fd = sd_bus_get_fd(bus);
+ source->pollfd.events = sd_bus_get_events(bus);
+
+ g_source_add_poll((GSource *)source, &source->pollfd);
+
+ return (GSource *)source;
+}
+
int main(int argc, char *argv[])
{
g_unix_signal_add(SIGTERM, quit_cb, NULL);
@@ -68,6 +118,7 @@ int main(int argc, char *argv[])
main_loop = g_main_loop_new(NULL, FALSE);
AppLauncher *launcher = app_launcher_get_default();
+
gint owner_id = g_bus_own_name(G_BUS_TYPE_SESSION, APPLAUNCH_DBUS_NAME,
G_BUS_NAME_OWNER_FLAGS_NONE, bus_acquired_cb,
name_acquired_cb, name_lost_cb,
diff --git a/src/meson.build b/src/meson.build
index 81d1f0c..761ad9b 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -17,6 +17,7 @@
applaunchd_deps = [
dependency('gobject-2.0'),
dependency('gio-unix-2.0'),
+ dependency('libsystemd'),
]
executable (
@@ -27,8 +28,8 @@ executable (
'main.c',
'app_info.c', 'app_info.h',
'app_launcher.c', 'app_launcher.h',
- 'dbus_activation_manager.c', 'dbus_activation_manager.h',
'process_manager.c', 'process_manager.h',
+ 'systemd_manager.c', 'systemd_manager.h',
'utils.c', 'utils.h',
],
dependencies : applaunchd_deps,
diff --git a/src/systemd_manager.c b/src/systemd_manager.c
new file mode 100644
index 0000000..3fae7fc
--- /dev/null
+++ b/src/systemd_manager.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2022 Konsulko Group
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <systemd/sd-bus.h>
+
+#include "app_launcher.h"
+#include "systemd_manager.h"
+
+struct _SystemdManager {
+ GObject parent_instance;
+};
+
+G_DEFINE_TYPE(SystemdManager, systemd_manager, G_TYPE_OBJECT);
+
+enum {
+ STARTED,
+ TERMINATED,
+ N_SIGNALS
+};
+static guint signals[N_SIGNALS];
+
+/*
+ * Application info structure, used for storing relevant data
+ * in the `running_apps` list
+ */
+struct systemd_runtime_data {
+ const gchar *esc_service;
+ SystemdManager *mgr;
+ sd_bus_slot *slot;
+};
+
+/*
+ * Initialization & cleanup functions
+ */
+
+static void systemd_manager_dispose(GObject *object)
+{
+ SystemdManager *self = APPLAUNCHD_SYSTEMD_MANAGER(object);
+
+ g_return_if_fail(APPLAUNCHD_IS_SYSTEMD_MANAGER(self));
+
+ G_OBJECT_CLASS(systemd_manager_parent_class)->dispose(object);
+}
+
+static void systemd_manager_finalize(GObject *object)
+{
+ G_OBJECT_CLASS(systemd_manager_parent_class)->finalize(object);
+}
+
+static void systemd_manager_class_init(SystemdManagerClass *klass)
+{
+ GObjectClass *object_class = (GObjectClass *)klass;
+
+ object_class->dispose = systemd_manager_dispose;
+ object_class->finalize = systemd_manager_finalize;
+
+ signals[STARTED] = g_signal_new("started", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, 0 ,
+ NULL, NULL, NULL, G_TYPE_NONE,
+ 1, G_TYPE_STRING);
+
+ signals[TERMINATED] = g_signal_new("terminated", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, 0 ,
+ NULL, NULL, NULL, G_TYPE_NONE,
+ 1, G_TYPE_STRING);
+}
+
+static void systemd_manager_init(SystemdManager *self)
+{
+}
+
+/*
+ * Internal callbacks
+ */
+
+/*
+ * This function is called when "PropertiesChanged" signal happens for
+ * the matched Unit - check its "ActiveState" to update the app status
+ */
+int systemd_manager_cb(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
+{
+ AppLauncher *launcher = app_launcher_get_default();
+ sd_bus_error err = SD_BUS_ERROR_NULL;
+ char* msg = NULL;
+ AppInfo *app_info = userdata;
+ struct systemd_runtime_data *data;
+
+ data = app_info_get_runtime_data(app_info);
+ if(!data)
+ {
+ g_critical("Couldn't find runtime data for %s!", app_info_get_app_id(app_info));
+ return 0;
+ }
+
+ sd_bus_get_property_string(
+ app_launcher_get_bus(launcher), /* bus */
+ "org.freedesktop.systemd1", /* destination */
+ data->esc_service, /* path */
+ "org.freedesktop.systemd1.Unit", /* interface */
+ "ActiveState", /* member */
+ &err,
+ &msg
+ );
+
+ if(!g_strcmp0(msg, "inactive"))
+ {
+ g_debug("Application %s has terminated", app_info_get_app_id(app_info));
+ app_info_set_status(app_info, APP_STATUS_INACTIVE);
+ app_info_set_runtime_data(app_info, NULL);
+
+ g_signal_emit(data->mgr, signals[TERMINATED], 0, app_info_get_app_id(app_info));
+ systemd_manager_free_runtime_data(data);
+ }
+ else if(!g_strcmp0(msg, "active"))
+ {
+ /* PropertiesChanged signal gets triggered multiple times, only handle it once */
+ if(app_info_get_status(app_info) != APP_STATUS_RUNNING)
+ {
+ g_debug("Application %s has started", app_info_get_app_id(app_info));
+ app_info_set_status(app_info, APP_STATUS_RUNNING);
+ g_signal_emit(data->mgr, signals[STARTED], 0, app_info_get_app_id(app_info));
+ }
+ }
+ return 0;
+}
+
+/*
+ * Public functions
+ */
+
+SystemdManager *systemd_manager_new(void)
+{
+ return g_object_new(APPLAUNCHD_TYPE_SYSTEMD_MANAGER, NULL);
+}
+
+/*
+ * Start an application by executing the provided command line.
+ */
+gboolean systemd_manager_start_app(SystemdManager *self,
+ AppInfo *app_info)
+{
+ g_return_val_if_fail(APPLAUNCHD_IS_SYSTEMD_MANAGER(self), FALSE);
+ g_return_val_if_fail(APPLAUNCHD_IS_APP_INFO(app_info), FALSE);
+
+ AppLauncher *launcher = app_launcher_get_default();
+ g_autofree gchar *service = NULL;
+ gchar *esc_service = NULL;
+ const gchar *app_id = app_info_get_app_id(app_info);
+ const gchar *command = app_info_get_command(app_info);
+ struct systemd_runtime_data *runtime_data;
+
+ sd_bus_error error = SD_BUS_ERROR_NULL;
+ sd_bus_message *m = NULL;
+ const char *path;
+ int r;
+
+ runtime_data = g_new0(struct systemd_runtime_data, 1);
+ if (!runtime_data) {
+ g_critical("Unable to allocate runtime data structure for '%s'", app_id);
+ return FALSE;
+ }
+
+ /* Compose the corresponding service name */
+ service = g_strdup_printf("agl-app@%s.service", command);
+ /* Get the escaped unit name in the systemd hierarchy */
+ sd_bus_path_encode("/org/freedesktop/systemd1/unit", service, &esc_service);
+
+ runtime_data->mgr = self;
+ runtime_data->esc_service = esc_service;
+
+ r = sd_bus_call_method(
+ app_launcher_get_bus(launcher), /* bus */
+ "org.freedesktop.systemd1", /* service to contact */
+ "/org/freedesktop/systemd1", /* object path */
+ "org.freedesktop.systemd1.Manager", /* interface name */
+ "StartUnit", /* method name */
+ &error, /* object to return error in */
+ &m, /* return message on success */
+ "ss", /* input signature */
+ service, /* first argument */
+ "replace" /* second argument */
+ );
+ if (r < 0) {
+ g_critical("Failed to issue method call: %s", error.message);
+ goto finish;
+ }
+
+ r = sd_bus_message_read(m, "o", &path);
+ if (r < 0) {
+ g_critical("Failed to parse response message: %s", strerror(-r));
+ goto finish;
+ }
+
+ r = sd_bus_match_signal(
+ app_launcher_get_bus(launcher), /* bus */
+ &runtime_data->slot, /* slot */
+ NULL, /* sender */
+ esc_service, /* path */
+ "org.freedesktop.DBus.Properties", /* interface */
+ "PropertiesChanged", /* member */
+ systemd_manager_cb, /* callback */
+ app_info /* userdata */
+ );
+ if (r < 0) {
+ g_critical("Failed to set match signal: %s", strerror(-r));
+ goto finish;
+ }
+
+ app_info_set_runtime_data(app_info, runtime_data);
+
+ /* The application is now starting, wait for notification to mark it running */
+ g_debug("Application %s is now being started", app_info_get_app_id(app_info));
+ app_info_set_status(app_info, APP_STATUS_STARTING);
+ return TRUE;
+
+finish:
+ if(runtime_data->esc_service)
+ g_free(runtime_data->esc_service);
+ g_free(runtime_data);
+ sd_bus_error_free(&error);
+ sd_bus_message_unref(m);
+ return FALSE;
+}
+
+void systemd_manager_free_runtime_data(gpointer data)
+{
+ struct systemd_runtime_data *runtime_data = data;
+
+ g_return_if_fail(runtime_data != NULL);
+
+ sd_bus_slot_unref(runtime_data->slot);
+ g_free(runtime_data->esc_service);
+ g_free(runtime_data);
+}
diff --git a/src/systemd_manager.h b/src/systemd_manager.h
new file mode 100644
index 0000000..a8a4ac7
--- /dev/null
+++ b/src/systemd_manager.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 Konsulko Group
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SYSTEMDMANAGER_H
+#define SYSTEMDMANAGER_H
+
+#include <glib-object.h>
+
+#include "app_info.h"
+
+G_BEGIN_DECLS
+
+#define APPLAUNCHD_TYPE_SYSTEMD_MANAGER systemd_manager_get_type()
+
+G_DECLARE_FINAL_TYPE(SystemdManager, systemd_manager,
+ APPLAUNCHD, SYSTEMD_MANAGER, GObject);
+
+SystemdManager *systemd_manager_new(void);
+
+gboolean systemd_manager_start_app(SystemdManager *self,
+ AppInfo *app_info);
+
+G_END_DECLS
+
+#endif