diff --git a/cinnamon-session/csm-manager.c b/cinnamon-session/csm-manager.c index 9d7e8d1..0812c78 100644 --- a/cinnamon-session/csm-manager.c +++ b/cinnamon-session/csm-manager.c @@ -953,6 +953,8 @@ _client_stop (const char *id, static void do_phase_exit (CsmManager *manager) { + csm_util_stop_systemd_unit ("cinnamon-session.target", NULL); + if (csm_store_size (manager->priv->clients) > 0) { csm_store_foreach (manager->priv->clients, (CsmStoreFunc)_client_stop, @@ -1523,6 +1525,7 @@ start_phase (CsmManager *manager) csm_xsmp_server_start_accepting_new_clients (manager->priv->xsmp_server); csm_exported_manager_emit_session_running (manager->priv->skeleton); update_idle (manager); + csm_util_start_systemd_unit ("cinnamon-session.target", NULL); break; case CSM_MANAGER_PHASE_QUERY_END_SESSION: csm_xsmp_server_stop_accepting_new_clients (manager->priv->xsmp_server); diff --git a/cinnamon-session/csm-util.c b/cinnamon-session/csm-util.c index 676b63a..029d71d 100644 --- a/cinnamon-session/csm-util.c +++ b/cinnamon-session/csm-util.c @@ -763,6 +763,201 @@ csm_util_update_user_environment (const char *variable, return environment_updated; } +static const char * const csm_systemd_env_denylist[] = { + "XDG_SESSION_ID", + "XDG_SESSION_TYPE", + "XDG_SESSION_CLASS", + "XDG_SESSION_DESKTOP", + "XDG_SEAT", + "XDG_VTNR", + "NOTIFY_SOCKET", + NULL +}; + +gboolean +csm_util_export_systemd_activation_environment (GError **error) +{ + GDBusConnection *connection; + GVariantBuilder builder; + GVariant *reply; + GError *bus_error = NULL; + GRegex *name_regex, *value_regex; + char **entry_names; + int i = 0; + gboolean environment_updated = FALSE; + + if (access ("/run/systemd/private", F_OK) != 0) + return TRUE; + + connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error); + if (connection == NULL) + return FALSE; + + name_regex = g_regex_new ("^[a-zA-Z_][a-zA-Z0-9_]*$", + G_REGEX_OPTIMIZE, 0, error); + if (name_regex == NULL) { + g_object_unref (connection); + return FALSE; + } + + value_regex = g_regex_new ("^([[:blank:]]|[^[:cntrl:]])*$", + G_REGEX_OPTIMIZE, 0, error); + if (value_regex == NULL) { + g_regex_unref (name_regex); + g_object_unref (connection); + return FALSE; + } + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("as")); + + entry_names = g_listenv (); + for (i = 0; entry_names[i] != NULL; i++) { + const char *entry_name = entry_names[i]; + const char *entry_value = g_getenv (entry_name); + + if (g_strv_contains (csm_systemd_env_denylist, entry_name)) + continue; + + if (!g_utf8_validate (entry_name, -1, NULL)) + continue; + + if (!g_regex_match (name_regex, entry_name, 0, NULL)) + continue; + + if (entry_value == NULL) + continue; + + if (!g_utf8_validate (entry_value, -1, NULL)) + continue; + + if (!g_regex_match (value_regex, entry_value, 0, NULL)) + continue; + + g_variant_builder_add (&builder, "s", + g_strdup_printf ("%s=%s", + entry_name, + entry_value)); + } + g_regex_unref (name_regex); + g_regex_unref (value_regex); + g_strfreev (entry_names); + + reply = g_dbus_connection_call_sync (connection, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "SetEnvironment", + g_variant_new ("(@as)", + g_variant_builder_end (&builder)), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, NULL, &bus_error); + + if (bus_error != NULL) { + g_warning ("csm_util_export_systemd_activation_environment: " + "failed to export to systemd: %s", + bus_error->message); + if (error != NULL) + g_propagate_error (error, bus_error); + else + g_error_free (bus_error); + } else { + environment_updated = TRUE; + g_variant_unref (reply); + } + + g_object_unref (connection); + return environment_updated; +} + +gboolean +csm_util_start_systemd_unit (const char *unit, + GError **error) +{ + GDBusConnection *connection; + GVariant *reply; + GError *bus_error = NULL; + gboolean ret = FALSE; + + if (access ("/run/systemd/private", F_OK) != 0) + return TRUE; + + connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error); + if (connection == NULL) + return FALSE; + + g_debug ("csm_util_start_systemd_unit: starting %s", unit); + + reply = g_dbus_connection_call_sync (connection, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartUnit", + g_variant_new ("(ss)", unit, "replace"), + G_VARIANT_TYPE ("(o)"), + G_DBUS_CALL_FLAGS_NONE, + -1, NULL, &bus_error); + + if (bus_error != NULL) { + g_warning ("csm_util_start_systemd_unit: failed to start %s: %s", + unit, bus_error->message); + if (error != NULL) + g_propagate_error (error, bus_error); + else + g_error_free (bus_error); + } else { + ret = TRUE; + g_variant_unref (reply); + } + + g_object_unref (connection); + return ret; +} + +gboolean +csm_util_stop_systemd_unit (const char *unit, + GError **error) +{ + GDBusConnection *connection; + GVariant *reply; + GError *bus_error = NULL; + gboolean ret = FALSE; + + if (access ("/run/systemd/private", F_OK) != 0) + return TRUE; + + connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error); + if (connection == NULL) + return FALSE; + + g_debug ("csm_util_stop_systemd_unit: stopping %s", unit); + + reply = g_dbus_connection_call_sync (connection, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StopUnit", + g_variant_new ("(ss)", unit, "replace"), + G_VARIANT_TYPE ("(o)"), + G_DBUS_CALL_FLAGS_NONE, + -1, NULL, &bus_error); + + if (bus_error != NULL) { + g_debug ("csm_util_stop_systemd_unit: failed to stop %s: %s", + unit, bus_error->message); + if (error != NULL) + g_propagate_error (error, bus_error); + else + g_error_free (bus_error); + } else { + ret = TRUE; + g_variant_unref (reply); + } + + g_object_unref (connection); + return ret; +} + void csm_util_setenv (const char *variable, const char *value) diff --git a/cinnamon-session/csm-util.h b/cinnamon-session/csm-util.h index 4d93c10..d1562f4 100644 --- a/cinnamon-session/csm-util.h +++ b/cinnamon-session/csm-util.h @@ -58,6 +58,13 @@ gboolean csm_util_export_user_environment (GError **error); void csm_util_setenv (const char *variable, const char *value); +gboolean csm_util_export_systemd_activation_environment (GError **error); + +gboolean csm_util_start_systemd_unit (const char *unit, + GError **error); +gboolean csm_util_stop_systemd_unit (const char *unit, + GError **error); + // main.c, exit mainloop void csm_quit (void); diff --git a/cinnamon-session/main.c b/cinnamon-session/main.c index d71fcc1..5c96bd4 100644 --- a/cinnamon-session/main.c +++ b/cinnamon-session/main.c @@ -265,6 +265,13 @@ main (int argc, char **argv) csm_util_export_activation_environment (NULL); csm_util_export_user_environment (NULL); + if (!csm_util_export_systemd_activation_environment (NULL)) { + g_warning ("main: failed to export environment to systemd --user; " + "some systemd-managed services may not start correctly"); + } + + csm_util_start_systemd_unit ("cinnamon-session.target", NULL); + { gchar *ibus_path; diff --git a/data/meson.build b/data/meson.build index b158e66..8a6c35b 100644 --- a/data/meson.build +++ b/data/meson.build @@ -20,3 +20,6 @@ install_data( # Re-compile gsettings meson.add_install_script('meson_install_schemas.py') + +subdir('systemd/user') + diff --git a/data/systemd/user/cinnamon-session.target b/data/systemd/user/cinnamon-session.target new file mode 100644 index 0000000..08b1607 --- /dev/null +++ b/data/systemd/user/cinnamon-session.target @@ -0,0 +1,10 @@ +[Unit] +Description=Cinnamon graphical session +Documentation=man:systemd.special(7) +Wants=graphical-session.target +Wants=graphical-session-pre.target +After=graphical-session-pre.target +After=graphical-session.target +PropagatesStopTo=graphical-session.target +PropagatesStopTo=graphical-session-pre.target +CollectMode=inactive-or-failed diff --git a/data/systemd/user/meson.build b/data/systemd/user/meson.build new file mode 100644 index 0000000..f628c81 --- /dev/null +++ b/data/systemd/user/meson.build @@ -0,0 +1,6 @@ +install_data( + [ + 'cinnamon-session.target', + ], + install_dir: systemduserunitdir, +) diff --git a/meson.build b/meson.build index a1ee4b3..a3bd48f 100644 --- a/meson.build +++ b/meson.build @@ -58,6 +58,15 @@ else endif conf.set('HAVE_LOGIND', logind.found()) +if libsystemdl.found() + systemduserunitdir = libsystemdl.get_variable( + pkgconfig: 'systemduserunitdir', + default_value: join_paths(get_option('prefix'), 'lib', 'systemd', 'user'), + ) +else + systemduserunitdir = join_paths(get_option('prefix'), 'lib', 'systemd', 'user') +endif + if gio_unix.found() and libelogind.found() elogind = declare_dependency(dependencies: [ gio_unix, libelogind ]) else