[gnome-desktop/benzea/systemd-start-scope] systemd: Add utility function to start a transient systemd scope



commit e7500db51d9d92d8d321009c4fc56b7a1635aae1
Author: Benjamin Berg <bberg redhat com>
Date:   Sat Nov 30 16:49:10 2019 +0100

    systemd: Add utility function to start a transient systemd scope
    
    In a systemd world, it is often a good idea to move launched
    applications outside of the own unit into one of their own. This helper
    allows to do so in a safe and consistent way.

 config.h.meson                   |   5 +-
 libgnome-desktop/gnome-systemd.c | 183 +++++++++++++++++++++++++++++++++++++++
 libgnome-desktop/gnome-systemd.h |  30 +++++++
 libgnome-desktop/meson.build     |   3 +
 meson.build                      |   3 +
 meson_options.txt                |   4 +
 6 files changed, 227 insertions(+), 1 deletion(-)
---
diff --git a/config.h.meson b/config.h.meson
index 2c72485a..7a8d8d96 100644
--- a/config.h.meson
+++ b/config.h.meson
@@ -12,10 +12,13 @@
 
 /* we have the timerfd_create(2) system call */
 #mesondefine HAVE_TIMERFD
-#
+
 /* Define to 1 if you have the `openat' function. */
 #mesondefine HAVE_OPENAT
 
+/* define if libsystemd is available */
+#mesondefine HAVE_SYSTEMD
+
 /* define if udev is available */
 #mesondefine HAVE_UDEV
 
diff --git a/libgnome-desktop/gnome-systemd.c b/libgnome-desktop/gnome-systemd.c
new file mode 100644
index 00000000..384b5f1c
--- /dev/null
+++ b/libgnome-desktop/gnome-systemd.c
@@ -0,0 +1,183 @@
+/* gnome-systemd.c
+ *
+ * Copyright 2019 Benjamin Berg <bberg redhat com>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file 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 Lesser General Public
+ * License along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-3.0-or-later
+ */
+
+#include "config.h"
+#include "gnome-systemd.h"
+
+#ifdef HAVE_SYSTEMD
+#include <errno.h>
+#include <systemd/sd-login.h>
+#endif
+
+#ifdef HAVE_SYSTEMD
+static void
+on_systemd_call_cb (GObject      *source,
+                    GAsyncResult *res,
+                    gpointer      user_data)
+{
+  g_autoptr (GVariant) reply = NULL;
+  g_autoptr (GError) error = NULL;
+  const gchar *command = user_data;
+
+  reply = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source),
+                                         res, &error);
+  if (error && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+    g_warning ("Could not issue '%s' systemd call", command);
+}
+#endif
+
+/**
+ * gnome_start_systemd_scope:
+ * @name: Name for the application
+ * @pid: The PID of the application
+ * @description: (nullable): A description to use for the unit, or %NULL
+ * @connection: (nullable): An #GDBusConnection to the session bus, or %NULL to use a new connection
+ * @error: #GError
+ *
+ * If the current process is running inside a user systemd instance, then move
+ * the launched PID into a transient scope. The given @name will be used to
+ * create a unit name. It should be the application ID for desktop files or
+ * the executable in all other cases.
+ *
+ * It is advisable to use this function every time where the started application
+ * can be considered reasonably independent of the launching application. Placing
+ * it in a scope creates proper separation between the programs rather than being
+ * considered a single entity by systemd.
+ *
+ * If given, the existing @connection is used for the sessions bus otherwise
+ * g_bus_get_sync() is called.
+ *
+ * It is always safe to call this function. Note that a successful return code
+ * does not imply that a unit has been created. It solely means that no error
+ * condition was hit sending the request.
+ *
+ * Returns: %TRUE on success, %FALSE
+ */
+gboolean
+gnome_start_systemd_scope (const char      *name,
+                           gint32           pid,
+                           const char      *description,
+                           GDBusConnection *connection,
+                           GError         **error)
+{
+#ifdef HAVE_SYSTEMD
+  GVariantBuilder builder;
+  const char *valid_chars =
+    "-._1234567890"
+    "abcdefghijklmnopqrstuvwxyz"
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+  g_autofree char *description_free = NULL;
+  g_autofree char *mangled_name = NULL;
+  g_autofree char *unit_name = NULL;
+  g_autofree char *own_unit = NULL;
+  gint res;
+  g_autoptr (GDBusConnection) connection_unref = NULL;
+
+  /* We cannot do anything if this process is not managed by the
+   * systemd user instance. */
+  res = sd_pid_get_user_unit (getpid (), &own_unit);
+  if (res == -ENODATA)
+    {
+      g_debug ("Not systemd managed, will not move PID %d into transient scope\n", pid);
+      return TRUE;
+    }
+  if (res < 0)
+    {
+      g_set_error (error,
+                   G_IO_ERROR,
+                   g_io_error_from_errno (-res),
+                   "Error fetching user unit for own pid: %d", -res);
+
+      return FALSE;
+    }
+
+  if (connection == NULL)
+    {
+      connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error);
+      connection_unref = connection;
+
+      if (connection == NULL)
+        return FALSE;
+    }
+
+  g_debug ("Trying to create transient scope for PID %d", pid);
+
+  /* Create a nice and (mangled) name to embed into the unit */
+  if (name == NULL)
+    name = "anonymous";
+  if (name[0] == '/')
+    name++;
+
+  mangled_name = g_str_to_ascii (name, "C");
+  g_strdelimit (mangled_name, "/", '-');
+  g_strcanon (mangled_name, valid_chars, '_');
+
+  if (description == NULL)
+    {
+      const char *app_name = g_get_application_name();
+
+      if (app_name)
+        description_free = g_strdup_printf ("Application launched by %s",
+                                            app_name);
+      description = description_free;
+    }
+
+  /* This needs to be unique, hopefully the pid will be enough. */
+  unit_name = g_strdup_printf ("gnome-launched-%s-%d.scope", mangled_name, pid);
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE ("(ssa(sv)a(sa(sv)))"));
+  g_variant_builder_add (&builder, "s", unit_name);
+  g_variant_builder_add (&builder, "s", "fail");
+
+  /* Note that gnome-session ships a drop-in to control further defaults. */
+  g_variant_builder_open (&builder, G_VARIANT_TYPE ("a(sv)"));
+  g_variant_builder_add (&builder,
+                         "(sv)",
+                         "Description",
+                         g_variant_new_string (description));
+  g_variant_builder_add (&builder,
+                         "(sv)",
+                         "PIDs",
+                          g_variant_new_fixed_array (G_VARIANT_TYPE_UINT32, &pid, 1, 4));
+
+  g_variant_builder_close (&builder);
+
+  g_variant_builder_open (&builder, G_VARIANT_TYPE ("a(sa(sv))"));
+  g_variant_builder_close (&builder);
+
+  g_dbus_connection_call (connection,
+                          "org.freedesktop.systemd1",
+                          "/org/freedesktop/systemd1",
+                          "org.freedesktop.systemd1.Manager",
+                          "StartTransientUnit",
+                          g_variant_builder_end (&builder),
+                          G_VARIANT_TYPE ("(o)"),
+                          G_DBUS_CALL_FLAGS_NO_AUTO_START,
+                          1000,
+                          NULL,
+                          on_systemd_call_cb,
+                          (gpointer) "StartTransientUnit");
+#else
+  g_debug ("Not creating transient scope for PID %d. Systemd support not compiled in.", pid);
+#endif
+
+  return TRUE;
+}
+
diff --git a/libgnome-desktop/gnome-systemd.h b/libgnome-desktop/gnome-systemd.h
new file mode 100644
index 00000000..fa2ae6da
--- /dev/null
+++ b/libgnome-desktop/gnome-systemd.h
@@ -0,0 +1,30 @@
+/* gnome-systemd.h
+ *
+ * Copyright 2019 Benjamin Berg <bberg redhat com>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file 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 Lesser General Public
+ * License along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+
+gboolean gnome_start_systemd_scope (const char      *name,
+                                    gint32           pid,
+                                    const char      *description,
+                                    GDBusConnection *connection,
+                                    GError         **error);
+
diff --git a/libgnome-desktop/meson.build b/libgnome-desktop/meson.build
index 8503215f..01d10b7e 100644
--- a/libgnome-desktop/meson.build
+++ b/libgnome-desktop/meson.build
@@ -17,6 +17,7 @@ introspection_sources = [
   'gnome-rr.c',
   'gnome-rr-config.c',
   'gnome-rr-output-info.c',
+  'gnome-systemd.c',
   'gnome-pnp-ids.c',
   'gnome-wall-clock.c',
   'gnome-xkb-info.c',
@@ -57,6 +58,7 @@ libgnome_desktop_headers = [
   'gnome-desktop-thumbnail.h',
   'gnome-rr.h',
   'gnome-rr-config.h',
+  'gnome-systemd.h',
   'gnome-pnp-ids.h',
   'gnome-wall-clock.h',
   'gnome-xkb-info.h',
@@ -75,6 +77,7 @@ gnome_desktop_deps = [
   glib_dep,
   gio_dep,
   gio_unix_dep,
+  libsystemd_dep,
   schemas_dep,
   xkb_config_dep,
   iso_codes_dep,
diff --git a/meson.build b/meson.build
index 6876c2c3..360d6600 100644
--- a/meson.build
+++ b/meson.build
@@ -50,6 +50,8 @@ xkb_config_dep = dependency('xkeyboard-config')
 iso_codes_dep = dependency('iso-codes')
 x_dep = dependency('x11')
 
+libsystemd_dep = dependency('libsystemd', required: get_option('systemd'))
+
 udev_dep = dependency('libudev', required: get_option('udev'))
 
 # Check for bubblewrap compatible platform
@@ -85,6 +87,7 @@ conf.set('ENABLE_SECCOMP', seccomp_dep.found())
 conf.set('HAVE_BWRAP', seccomp_dep.found())
 conf.set('_GNU_SOURCE', seccomp_dep.found())
 
+conf.set('HAVE_SYSTEMD', libsystemd_dep.found())
 conf.set('HAVE_UDEV', udev_dep.found())
 
 conf.set('HAVE_TIMERFD', cc.has_function('timerfd_create'))
diff --git a/meson_options.txt b/meson_options.txt
index e3402a11..c0fea3f8 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -21,6 +21,10 @@ option('udev',
   type: 'feature', description: 'Udev support'
 )
 
+option('systemd',
+  type: 'feature', description: 'Systemd integration support'
+)
+
 option('gtk_doc',
   type: 'boolean', value: false, description: 'Build API reference'
 )


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]