summaryrefslogtreecommitdiffstats
path: root/gnome-session/gsm-util.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:49:37 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:49:37 +0000
commit35504d91654321ff2b378229ff13150f53d5aad2 (patch)
treecb85edefc751b37c8423d78c5e5888f42cc01e4b /gnome-session/gsm-util.c
parentInitial commit. (diff)
downloadgnome-session-35504d91654321ff2b378229ff13150f53d5aad2.tar.xz
gnome-session-35504d91654321ff2b378229ff13150f53d5aad2.zip
Adding upstream version 43.0.upstream/43.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gnome-session/gsm-util.c')
-rw-r--r--gnome-session/gsm-util.c863
1 files changed, 863 insertions, 0 deletions
diff --git a/gnome-session/gsm-util.c b/gnome-session/gsm-util.c
new file mode 100644
index 0000000..f1e6489
--- /dev/null
+++ b/gnome-session/gsm-util.c
@@ -0,0 +1,863 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ * gsm-util.c
+ * Copyright (C) 2008 Lucas Rocha.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <gio/gio.h>
+
+#include "gsm-util.h"
+
+static gchar *_saved_session_dir = NULL;
+static gchar **child_environment;
+
+/* These are variables that will not be passed on to subprocesses
+ * (either directly, via systemd or DBus).
+ * Some of these are blacklisted as they might end up in the wrong session
+ * (e.g. XDG_VTNR), others because they simply must never be passed on
+ * (NOTIFY_SOCKET).
+ */
+static const char * const variable_blacklist[] = {
+ "NOTIFY_SOCKET",
+ "XDG_SEAT",
+ "XDG_SESSION_ID",
+ "XDG_VTNR",
+ NULL
+};
+
+/* The following is copied from GDMs spawn_session function.
+ *
+ * Environment variables listed here will be copied into the user's service
+ * environments if they are set in gnome-session's environment. If they are
+ * not set in gnome-session's environment, they will be removed from the
+ * service environments. This is to protect against environment variables
+ * leaking from previous sessions (e.g. when switching from classic to
+ * default GNOME $GNOME_SHELL_SESSION_MODE will become unset).
+ */
+static const char * const variable_unsetlist[] = {
+ "DISPLAY",
+ "XAUTHORITY",
+ "WAYLAND_DISPLAY",
+ "WAYLAND_SOCKET",
+ "GNOME_SHELL_SESSION_MODE",
+ "GNOME_SETUP_DISPLAY",
+
+ /* None of the LC_* variables should survive a logout/login */
+ "LC_CTYPE",
+ "LC_NUMERIC",
+ "LC_TIME",
+ "LC_COLLATE",
+ "LC_MONETARY",
+ "LC_MESSAGES",
+ "LC_PAPER",
+ "LC_NAME",
+ "LC_ADDRESS",
+ "LC_TELEPHONE",
+ "LC_MEASUREMENT",
+ "LC_IDENTIFICATION",
+ "LC_ALL",
+
+ NULL
+};
+
+char *
+gsm_util_find_desktop_file_for_app_name (const char *name,
+ gboolean look_in_saved_session,
+ gboolean autostart_first)
+{
+ char *app_path;
+ char **app_dirs;
+ GKeyFile *key_file;
+ char *desktop_file;
+ int i;
+
+ app_path = NULL;
+
+ app_dirs = gsm_util_get_desktop_dirs (look_in_saved_session, autostart_first);
+
+ key_file = g_key_file_new ();
+
+ desktop_file = g_strdup_printf ("%s.desktop", name);
+
+ g_debug ("GsmUtil: Looking for file '%s'", desktop_file);
+
+ for (i = 0; app_dirs[i] != NULL; i++) {
+ g_debug ("GsmUtil: Looking in '%s'", app_dirs[i]);
+ }
+
+ g_key_file_load_from_dirs (key_file,
+ desktop_file,
+ (const char **) app_dirs,
+ &app_path,
+ G_KEY_FILE_NONE,
+ NULL);
+
+ if (app_path != NULL) {
+ g_debug ("GsmUtil: found in XDG dirs: '%s'", app_path);
+ }
+
+ /* look for gnome vendor prefix */
+ if (app_path == NULL) {
+ g_free (desktop_file);
+ desktop_file = g_strdup_printf ("gnome-%s.desktop", name);
+
+ g_key_file_load_from_dirs (key_file,
+ desktop_file,
+ (const char **) app_dirs,
+ &app_path,
+ G_KEY_FILE_NONE,
+ NULL);
+ if (app_path != NULL) {
+ g_debug ("GsmUtil: found in XDG dirs: '%s'", app_path);
+ }
+ }
+
+ g_free (desktop_file);
+ g_key_file_free (key_file);
+
+ g_strfreev (app_dirs);
+
+ return app_path;
+}
+
+static gboolean
+ensure_dir_exists (const char *dir)
+{
+ if (g_mkdir_with_parents (dir, 0700) == 0)
+ return TRUE;
+
+ g_warning ("GsmSessionSave: Failed to create directory %s: %s", dir, strerror (errno));
+
+ return FALSE;
+}
+
+gchar *
+gsm_util_get_empty_tmp_session_dir (void)
+{
+ char *tmp;
+ gboolean exists;
+
+ tmp = g_build_filename (g_get_user_config_dir (),
+ "gnome-session",
+ "saved-session.new",
+ NULL);
+
+ exists = ensure_dir_exists (tmp);
+
+ if (G_UNLIKELY (!exists)) {
+ g_warning ("GsmSessionSave: could not create directory for saved session: %s", tmp);
+ g_free (tmp);
+ return NULL;
+ } else {
+ /* make sure it's empty */
+ GDir *dir;
+ const char *filename;
+
+ dir = g_dir_open (tmp, 0, NULL);
+ if (dir) {
+ while ((filename = g_dir_read_name (dir))) {
+ char *path = g_build_filename (tmp, filename,
+ NULL);
+ g_unlink (path);
+ }
+ g_dir_close (dir);
+ }
+ }
+
+ return tmp;
+}
+
+const gchar *
+gsm_util_get_saved_session_dir (void)
+{
+ if (_saved_session_dir == NULL) {
+ gboolean exists;
+
+ _saved_session_dir =
+ g_build_filename (g_get_user_config_dir (),
+ "gnome-session",
+ "saved-session",
+ NULL);
+
+ exists = ensure_dir_exists (_saved_session_dir);
+
+ if (G_UNLIKELY (!exists)) {
+ static gboolean printed_warning = FALSE;
+
+ if (!printed_warning) {
+ g_warning ("GsmSessionSave: could not create directory for saved session: %s", _saved_session_dir);
+ printed_warning = TRUE;
+ }
+
+ _saved_session_dir = NULL;
+
+ return NULL;
+ }
+ }
+
+ return _saved_session_dir;
+}
+
+static char ** autostart_dirs;
+
+void
+gsm_util_set_autostart_dirs (char ** dirs)
+{
+ autostart_dirs = g_strdupv (dirs);
+}
+
+static char **
+gsm_util_get_standard_autostart_dirs (void)
+{
+ GPtrArray *dirs;
+ const char * const *system_config_dirs;
+ const char * const *system_data_dirs;
+ int i;
+
+ dirs = g_ptr_array_new ();
+
+ g_ptr_array_add (dirs,
+ g_build_filename (g_get_user_config_dir (),
+ "autostart", NULL));
+
+ system_data_dirs = g_get_system_data_dirs ();
+ for (i = 0; system_data_dirs[i]; i++) {
+ g_ptr_array_add (dirs,
+ g_build_filename (system_data_dirs[i],
+ "gnome", "autostart", NULL));
+ }
+
+ system_config_dirs = g_get_system_config_dirs ();
+ for (i = 0; system_config_dirs[i]; i++) {
+ g_ptr_array_add (dirs,
+ g_build_filename (system_config_dirs[i],
+ "autostart", NULL));
+ }
+
+ g_ptr_array_add (dirs, NULL);
+
+ return (char **) g_ptr_array_free (dirs, FALSE);
+}
+
+char **
+gsm_util_get_autostart_dirs ()
+{
+ if (autostart_dirs) {
+ return g_strdupv ((char **)autostart_dirs);
+ }
+
+ return gsm_util_get_standard_autostart_dirs ();
+}
+
+char **
+gsm_util_get_app_dirs ()
+{
+ GPtrArray *dirs;
+ const char * const *system_data_dirs;
+ int i;
+
+ dirs = g_ptr_array_new ();
+
+ g_ptr_array_add (dirs,
+ g_build_filename (g_get_user_data_dir (),
+ "applications",
+ NULL));
+
+ system_data_dirs = g_get_system_data_dirs ();
+ for (i = 0; system_data_dirs[i]; i++) {
+ g_ptr_array_add (dirs,
+ g_build_filename (system_data_dirs[i],
+ "applications",
+ NULL));
+ }
+
+ g_ptr_array_add (dirs, NULL);
+
+ return (char **) g_ptr_array_free (dirs, FALSE);
+}
+
+char **
+gsm_util_get_desktop_dirs (gboolean include_saved_session,
+ gboolean autostart_first)
+{
+ char **apps;
+ char **autostart;
+ char **standard_autostart;
+ char **result;
+ int size;
+ int i;
+
+ apps = gsm_util_get_app_dirs ();
+ autostart = gsm_util_get_autostart_dirs ();
+
+ /* Still, check the standard autostart dirs for things like fulfilling session reqs,
+ * if using a non-standard autostart dir for autostarting */
+ if (autostart_dirs != NULL)
+ standard_autostart = gsm_util_get_standard_autostart_dirs ();
+ else
+ standard_autostart = NULL;
+
+ size = 0;
+ for (i = 0; apps[i] != NULL; i++) { size++; }
+ for (i = 0; autostart[i] != NULL; i++) { size++; }
+ if (autostart_dirs != NULL)
+ for (i = 0; standard_autostart[i] != NULL; i++) { size++; }
+ if (include_saved_session)
+ size += 1;
+
+ result = g_new (char *, size + 1); /* including last NULL */
+
+ size = 0;
+
+ if (autostart_first) {
+ if (include_saved_session)
+ result[size++] = g_strdup (gsm_util_get_saved_session_dir ());
+
+ for (i = 0; autostart[i] != NULL; i++, size++) {
+ result[size] = autostart[i];
+ }
+ if (standard_autostart != NULL) {
+ for (i = 0; standard_autostart[i] != NULL; i++, size++) {
+ result[size] = standard_autostart[i];
+ }
+ }
+ for (i = 0; apps[i] != NULL; i++, size++) {
+ result[size] = apps[i];
+ }
+ } else {
+ for (i = 0; apps[i] != NULL; i++, size++) {
+ result[size] = apps[i];
+ }
+ if (standard_autostart != NULL) {
+ for (i = 0; standard_autostart[i] != NULL; i++, size++) {
+ result[size] = standard_autostart[i];
+ }
+ }
+ for (i = 0; autostart[i] != NULL; i++, size++) {
+ result[size] = autostart[i];
+ }
+
+ if (include_saved_session)
+ result[size++] = g_strdup (gsm_util_get_saved_session_dir ());
+ }
+
+ g_free (apps);
+ g_free (autostart);
+ g_free (standard_autostart);
+
+ result[size] = NULL;
+
+ return result;
+}
+
+gboolean
+gsm_util_text_is_blank (const char *str)
+{
+ if (str == NULL) {
+ return TRUE;
+ }
+
+ while (*str) {
+ if (!isspace(*str)) {
+ return FALSE;
+ }
+
+ str++;
+ }
+
+ return TRUE;
+}
+
+/**
+ * gsm_util_init_error:
+ * @fatal: whether or not the error is fatal to the login session
+ * @format: printf-style error message format
+ * @...: error message args
+ *
+ * Displays the error message to the user. If @fatal is %TRUE, gsm
+ * will exit after displaying the message.
+ *
+ * This should be called for major errors that occur before the
+ * session is up and running. (Notably, it positions the dialog box
+ * itself, since no window manager will be running yet.)
+ **/
+void
+gsm_util_init_error (gboolean fatal,
+ const char *format, ...)
+{
+ char *msg;
+ va_list args;
+ gchar *argv[13];
+
+ va_start (args, format);
+ msg = g_strdup_vprintf (format, args);
+ va_end (args);
+
+ argv[0] = "zenity";
+ argv[1] = "--error";
+ argv[2] = "--class";
+ argv[3] = "mutter-dialog";
+ argv[4] = "--title";
+ argv[5] = "\"\"";
+ argv[6] = "--text";
+ argv[7] = msg;
+ argv[8] = "--icon-name";
+ argv[9] = "face-sad-symbolic";
+ argv[10] = "--ok-label";
+ argv[11] = _("_Log out");
+ argv[12] = NULL;
+
+ g_spawn_sync (NULL, argv, child_environment, 0, NULL, NULL, NULL, NULL, NULL, NULL);
+
+ g_free (msg);
+
+ if (fatal) {
+ exit (1);
+ }
+}
+
+/**
+ * gsm_util_generate_startup_id:
+ *
+ * Generates a new SM client ID.
+ *
+ * Return value: an SM client ID.
+ **/
+char *
+gsm_util_generate_startup_id (void)
+{
+ static int sequence = -1;
+ static guint rand1 = 0;
+ static guint rand2 = 0;
+ static pid_t pid = 0;
+ struct timeval tv;
+
+ /* The XSMP spec defines the ID as:
+ *
+ * Version: "1"
+ * Address type and address:
+ * "1" + an IPv4 address as 8 hex digits
+ * "2" + a DECNET address as 12 hex digits
+ * "6" + an IPv6 address as 32 hex digits
+ * Time stamp: milliseconds since UNIX epoch as 13 decimal digits
+ * Process-ID type and process-ID:
+ * "1" + POSIX PID as 10 decimal digits
+ * Sequence number as 4 decimal digits
+ *
+ * XSMP client IDs are supposed to be globally unique: if
+ * SmsGenerateClientID() is unable to determine a network
+ * address for the machine, it gives up and returns %NULL.
+ * GNOME and KDE have traditionally used a fourth address
+ * format in this case:
+ * "0" + 16 random hex digits
+ *
+ * We don't even bother trying SmsGenerateClientID(), since the
+ * user's IP address is probably "192.168.1.*" anyway, so a random
+ * number is actually more likely to be globally unique.
+ */
+
+ if (!rand1) {
+ rand1 = g_random_int ();
+ rand2 = g_random_int ();
+ pid = getpid ();
+ }
+
+ sequence = (sequence + 1) % 10000;
+ gettimeofday (&tv, NULL);
+ return g_strdup_printf ("10%.04x%.04x%.10lu%.3u%.10lu%.4d",
+ rand1,
+ rand2,
+ (unsigned long) tv.tv_sec,
+ (unsigned) tv.tv_usec,
+ (unsigned long) pid,
+ sequence);
+}
+
+static gboolean
+gsm_util_update_activation_environment (const char *variable,
+ const char *value,
+ GError **error)
+{
+ GDBusConnection *connection;
+ gboolean environment_updated;
+ GVariantBuilder builder;
+ GVariant *reply;
+ GError *bus_error = NULL;
+
+ environment_updated = FALSE;
+ connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error);
+
+ if (connection == NULL) {
+ return FALSE;
+ }
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{ss}"));
+ g_variant_builder_add (&builder, "{ss}", variable, value);
+
+ reply = g_dbus_connection_call_sync (connection,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "UpdateActivationEnvironment",
+ g_variant_new ("(@a{ss})",
+ g_variant_builder_end (&builder)),
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, NULL, &bus_error);
+
+ if (bus_error != NULL) {
+ g_propagate_error (error, bus_error);
+ } else {
+ environment_updated = TRUE;
+ g_variant_unref (reply);
+ }
+
+ g_clear_object (&connection);
+
+ return environment_updated;
+}
+
+gboolean
+gsm_util_export_activation_environment (GError **error)
+{
+
+ GDBusConnection *connection;
+ gboolean environment_updated = FALSE;
+ char **entry_names;
+ int i = 0;
+ GVariantBuilder builder;
+ GRegex *name_regex, *value_regex;
+ GVariant *reply;
+ GError *bus_error = NULL;
+
+ 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) {
+ return FALSE;
+ }
+
+ value_regex = g_regex_new ("^(?:[ \t\n]|[^[:cntrl:]])*$", G_REGEX_OPTIMIZE, 0, error);
+
+ if (value_regex == NULL) {
+ return FALSE;
+ }
+
+ if (child_environment == NULL) {
+ child_environment = g_listenv ();
+ }
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{ss}"));
+ for (entry_names = g_listenv (); entry_names[i] != NULL; i++) {
+ const char *entry_name = entry_names[i];
+ const char *entry_value = g_getenv (entry_name);
+
+ if (g_strv_contains (variable_blacklist, entry_name))
+ continue;
+
+ if (!g_utf8_validate (entry_name, -1, NULL) ||
+ !g_regex_match (name_regex, entry_name, 0, NULL) ||
+ !g_utf8_validate (entry_value, -1, NULL) ||
+ !g_regex_match (value_regex, entry_value, 0, NULL)) {
+
+ g_message ("Environment variable is unsafe to export to dbus: %s", entry_name);
+ continue;
+ }
+
+ child_environment = g_environ_setenv (child_environment,
+ entry_name, entry_value,
+ TRUE);
+ g_variant_builder_add (&builder, "{ss}", 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.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "UpdateActivationEnvironment",
+ g_variant_new ("(@a{ss})",
+ g_variant_builder_end (&builder)),
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, NULL, &bus_error);
+
+ if (bus_error != NULL) {
+ g_propagate_error (error, bus_error);
+ } else {
+ environment_updated = TRUE;
+ g_variant_unref (reply);
+ }
+
+ g_clear_object (&connection);
+
+ return environment_updated;
+}
+
+#ifdef HAVE_SYSTEMD
+gboolean
+gsm_util_export_user_environment (GError **error)
+{
+
+ GDBusConnection *connection;
+ gboolean environment_updated = FALSE;
+ char **entries;
+ int i = 0;
+ GVariantBuilder builder;
+ GRegex *regex;
+ GVariant *reply;
+ GError *bus_error = NULL;
+
+ connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error);
+
+ if (connection == NULL) {
+ return FALSE;
+ }
+
+ regex = g_regex_new ("^[a-zA-Z_][a-zA-Z0-9_]*=(?:[ \t\n]|[^[:cntrl:]])*$", G_REGEX_OPTIMIZE, 0, error);
+
+ if (regex == NULL) {
+ return FALSE;
+ }
+
+ entries = g_get_environ ();
+
+ for (i = 0; variable_blacklist[i] != NULL; i++)
+ entries = g_environ_unsetenv (entries, variable_blacklist[i]);
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("(asas)"));
+
+ g_variant_builder_open (&builder, G_VARIANT_TYPE ("as"));
+ for (i = 0; variable_unsetlist[i] != NULL; i++)
+ g_variant_builder_add (&builder, "s", variable_unsetlist[i]);
+ for (i = 0; variable_blacklist[i] != NULL; i++)
+ g_variant_builder_add (&builder, "s", variable_blacklist[i]);
+ g_variant_builder_close (&builder);
+
+ g_variant_builder_open (&builder, G_VARIANT_TYPE ("as"));
+ for (i = 0; entries[i] != NULL; i++) {
+ const char *entry = entries[i];
+
+ if (!g_utf8_validate (entry, -1, NULL) ||
+ !g_regex_match (regex, entry, 0, NULL)) {
+
+ g_message ("Environment entry is unsafe to upload into user environment: %s", entry);
+ continue;
+ }
+
+ g_variant_builder_add (&builder, "s", entry);
+ }
+ g_variant_builder_close (&builder);
+ g_regex_unref (regex);
+
+ g_strfreev (entries);
+
+ reply = g_dbus_connection_call_sync (connection,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "UnsetAndSetEnvironment",
+ g_variant_builder_end (&builder),
+ NULL,
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ -1, NULL, &bus_error);
+
+ if (bus_error != NULL) {
+ g_propagate_error (error, bus_error);
+ } else {
+ environment_updated = TRUE;
+ g_variant_unref (reply);
+ }
+
+ g_clear_object (&connection);
+
+ return environment_updated;
+}
+
+static gboolean
+gsm_util_update_user_environment (const char *variable,
+ const char *value,
+ GError **error)
+{
+ GDBusConnection *connection;
+ gboolean environment_updated;
+ char *entry;
+ GVariantBuilder builder;
+ GVariant *reply;
+ GError *bus_error = NULL;
+
+ environment_updated = FALSE;
+ connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error);
+
+ if (connection == NULL) {
+ return FALSE;
+ }
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
+ entry = g_strdup_printf ("%s=%s", variable, value);
+ g_variant_builder_add (&builder, "s", entry);
+ g_free (entry);
+
+ 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_NO_AUTO_START,
+ -1, NULL, &bus_error);
+
+ if (bus_error != NULL) {
+ g_propagate_error (error, bus_error);
+ } else {
+ environment_updated = TRUE;
+ g_variant_unref (reply);
+ }
+
+ g_clear_object (&connection);
+
+ return environment_updated;
+}
+
+gboolean
+gsm_util_start_systemd_unit (const char *unit,
+ const char *mode,
+ GError **error)
+{
+ g_autoptr(GDBusConnection) connection = NULL;
+ g_autoptr(GVariant) reply = NULL;
+ GError *bus_error = NULL;
+
+ connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error);
+
+ if (connection == NULL)
+ return FALSE;
+
+ reply = g_dbus_connection_call_sync (connection,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "StartUnit",
+ g_variant_new ("(ss)",
+ unit, mode),
+ NULL,
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ -1, NULL, &bus_error);
+
+ if (bus_error != NULL) {
+ g_propagate_error (error, bus_error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+gsm_util_systemd_reset_failed (GError **error)
+{
+ g_autoptr(GDBusConnection) connection = NULL;
+ g_autoptr(GVariant) reply = NULL;
+ GError *bus_error = NULL;
+
+ connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error);
+
+ if (connection == NULL)
+ return FALSE;
+
+ reply = g_dbus_connection_call_sync (connection,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "ResetFailed",
+ NULL,
+ NULL,
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ -1, NULL, &bus_error);
+
+ if (bus_error != NULL) {
+ g_propagate_error (error, bus_error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+#endif
+
+void
+gsm_util_setenv (const char *variable,
+ const char *value)
+{
+ GError *error = NULL;
+
+ if (child_environment == NULL)
+ child_environment = g_listenv ();
+
+ if (!value)
+ child_environment = g_environ_unsetenv (child_environment, variable);
+ else
+ child_environment = g_environ_setenv (child_environment, variable, value, TRUE);
+
+ /* If this fails it isn't fatal, it means some things like session
+ * management and keyring won't work in activated clients.
+ */
+ if (!gsm_util_update_activation_environment (variable, value, &error)) {
+ g_warning ("Could not make bus activated clients aware of %s=%s environment variable: %s", variable, value, error->message);
+ g_clear_error (&error);
+ }
+
+#ifdef HAVE_SYSTEMD
+ /* If this fails, the system user session won't get the updated environment
+ */
+ if (!gsm_util_update_user_environment (variable, value, &error)) {
+ g_debug ("Could not make systemd aware of %s=%s environment variable: %s", variable, value, error->message);
+ g_clear_error (&error);
+ }
+#endif
+}
+
+const char * const *
+gsm_util_listenv (void)
+{
+ return (const char * const *) child_environment;
+
+}
+
+const char * const *
+gsm_util_get_variable_blacklist (void)
+{
+ return variable_blacklist;
+}