Skip to content

Commit

Permalink
Add gnome-systemd
Browse files Browse the repository at this point in the history
This was added by GNOME in gnome-desktop to make it easy to create
scopes and run processes with predefined defaults.

The idea was to be able to lower processes timeout when killed or to
implement OOM features.

More info at:

https://gitlab.gnome.org/GNOME/gnome-desktop/-/merge_requests/58
https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/863
https://gitlab.gnome.org/GNOME/gnome-session/-/merge_requests/32
https://gitlab.gnome.org/GNOME/gnome-settings-daemon/-/merge_requests/147

systemd/systemd#14202


This also results in moving processes under the Cinnamon process (which
then acts as parent process).

Unless/until this fixes an actual identified issue for us
or provides significant advantages we're not using it in Cinnamon.

The decision was nonetheless to leave it here in cinnamon-desktop to
reduce the difference with gnome-desktop for maintenance purpose and
to have it in if we ever want to use it in the future.
  • Loading branch information
mtwebster authored and clefebvre committed Nov 9, 2023
1 parent 610a006 commit 7eadfb1
Show file tree
Hide file tree
Showing 6 changed files with 321 additions and 0 deletions.
1 change: 1 addition & 0 deletions debian/control
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Build-Depends:
libglib2.0-dev (>= 2.37.3),
libgtk-3-dev (>= 3.3.16),
libpulse-dev (>= 12.99.3),
libsystemd-dev,
libx11-dev,
libxext-dev,
libxkbfile-dev,
Expand Down
270 changes: 270 additions & 0 deletions libcinnamon-desktop/gnome-systemd.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
/* gnome-systemd.c
*
* Copyright 2019 Benjamin Berg <[email protected]>
*
* 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"

#define GNOME_DESKTOP_USE_UNSTABLE_API
#include "gnome-systemd.h"

#ifdef HAVE_SYSTEMD
#include <errno.h>
#include <systemd/sd-login.h>
#endif

#ifdef HAVE_SYSTEMD
typedef struct {
char *name;
char *description;
gint32 pid;
} StartSystemdScopeData;

static void
start_systemd_scope_data_free (StartSystemdScopeData *data)
{
g_clear_pointer (&data->name, g_free);
g_clear_pointer (&data->description, g_free);
g_free (data);
}

static void
on_start_transient_unit_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr (GTask) task = G_TASK (user_data);
g_autoptr (GVariant) reply = NULL;
GError *error = NULL;
StartSystemdScopeData *task_data = g_task_get_task_data (task);

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 create transient scope for PID %d: %s",
task_data->pid, error->message);
g_task_return_error (task, error);
return;
}

g_debug ("Created transient scope for PID %d", task_data->pid);

g_task_return_boolean (task, TRUE);
}

static void
start_systemd_scope (GDBusConnection *connection, GTask *task)
{
GVariantBuilder builder;
g_autofree char *unit_name = NULL;
StartSystemdScopeData *task_data = g_task_get_task_data (task);

g_assert (task_data != NULL);

/* This needs to be unique, hopefully the pid will be enough. */
unit_name = g_strdup_printf ("gnome-launched-%s-%d.scope", task_data->name, task_data->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)"));
if (task_data->description)
g_variant_builder_add (&builder,
"(sv)",
"Description",
g_variant_new_string (task_data->description));
g_variant_builder_add (&builder,
"(sv)",
"PIDs",
g_variant_new_fixed_array (G_VARIANT_TYPE_UINT32, &task_data->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,
g_task_get_cancellable (task),
on_start_transient_unit_cb,
task);
}

static void
on_bus_gotten_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GTask) task = G_TASK (user_data);
g_autoptr(GDBusConnection) connection = NULL;
GError *error = NULL;

connection = g_bus_get_finish (res, &error);
if (error)
{
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
g_warning ("Could not get session bus: %s", error->message);

g_task_return_error (task, error);
return;
}

start_systemd_scope (connection, g_steal_pointer (&task));
}
#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
* @cancellable: (nullable): #GCancellable to use
* @callback: (nullable): Callback to call when the operation is done
* @user_data: Data to be passed to @callback
*
* 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.
*
* 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.
*
* If @connection is %NULL then g_dbus_get() will be called internally.
*
* Note that most callers will not need to handle errors. As such, it is normal
* to pass a %NULL @callback.
*
* Stability: unstable
*/
void
gnome_start_systemd_scope (const char *name,
gint32 pid,
const char *description,
GDBusConnection *connection,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;

#ifdef HAVE_SYSTEMD
g_autofree char *own_unit = NULL;
const char *valid_chars =
"-._1234567890"
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
StartSystemdScopeData *task_data;
gint res;

task = g_task_new (NULL, cancellable, callback, user_data);
task_data = g_new0 (StartSystemdScopeData, 1);

task_data->pid = pid;

/* Create a nice and (mangled) name to embed into the unit */
if (name == NULL)
name = "anonymous";

if (name[0] == '/')
name++;

task_data->name = g_str_to_ascii (name, "C");
g_strdelimit (task_data->name, "/", '-');
g_strcanon (task_data->name, valid_chars, '_');

task_data->description = g_strdup (description);
if (task_data->description == NULL)
{
const char *app_name = g_get_application_name();

if (app_name)
task_data->description = g_strdup_printf ("Application launched by %s",
app_name);
}

g_task_set_task_data (task, task_data, (GDestroyNotify) start_systemd_scope_data_free);

/* 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);
g_task_return_boolean (task, TRUE);

return;
}
if (res < 0)
{
g_warning ("Error fetching user unit for own pid: %d\n", -res);
g_task_return_new_error (task,
G_IO_ERROR,
g_io_error_from_errno (-res),
"Error fetching user unit for own pid: %d", -res);

return;
}

if (connection == NULL)
g_bus_get (G_BUS_TYPE_SESSION, cancellable, on_bus_gotten_cb, g_steal_pointer (&task));
else
start_systemd_scope (connection, g_steal_pointer (&task));
#else
g_debug ("Not creating transient scope for PID %d. Systemd support not compiled in.", pid);

task = g_task_new (NULL, cancellable, callback, user_data);
g_task_return_boolean (task, TRUE);
#endif
}

/**
* gnome_start_systemd_scope_finish:
* @res: A #GAsyncResult
* @error: Return location for errors, or %NULL to ignore
*
* Finish an asynchronous operation to create a transient scope that was
* started with gnome_start_systemd_scope().
*
* 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: %FALSE on error, %TRUE otherwise
*/
gboolean
gnome_start_systemd_scope_finish (GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
41 changes: 41 additions & 0 deletions libcinnamon-desktop/gnome-systemd.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* gnome-systemd.h
*
* Copyright 2019 Benjamin Berg <[email protected]>
*
* 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
*/

#ifndef _GNOME_SYSTEMD_H
#define _GNOME_SYSTEMD_H

#ifndef GNOME_DESKTOP_USE_UNSTABLE_API
#error GnomeDesktopSystemd is unstable API. You must define GNOME_DESKTOP_USE_UNSTABLE_API before including gnome-systemd.h
#endif

#include <gio/gio.h>

void gnome_start_systemd_scope (const char *name,
gint32 pid,
const char *description,
GDBusConnection *connection,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);

gboolean gnome_start_systemd_scope_finish (GAsyncResult *res,
GError **error);

#endif /* _GNOME_SYSTEMD_H */
2 changes: 2 additions & 0 deletions libcinnamon-desktop/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ libcinnamon_desktop_gir_sources = [
'gnome-rr-labeler.c',
'gnome-rr-output-info.c',
'gnome-rr.c',
'gnome-systemd.c',
'gnome-thumbnail-pixbuf-utils.c',
'gnome-wall-clock.c',
'gnome-xkb-info.c',
Expand Down Expand Up @@ -42,6 +43,7 @@ libcinnamon_desktop_headers = [
'gnome-rr-config.h',
'gnome-rr-labeler.h',
'gnome-rr.h',
'gnome-systemd.h',
'gnome-wall-clock.h',
'gnome-xkb-info.h',
]
Expand Down
4 changes: 4 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ gdk_pixb= dependency('gdk-pixbuf-2.0', version: '>=2.22.0')
gio = dependency('gio-2.0', version: '>=2.37.3')
glib = dependency('glib-2.0', version: '>=2.37.3')
gtk = dependency('gtk+-3.0', version: '>=3.3.16')
systemd = dependency('libsystemd', required: get_option('systemd'))

x11 = dependency('x11')
xext = dependency('xext', version: '>=1.1')
Expand All @@ -48,6 +49,7 @@ cinnamon_deps = [
glib,
gtk,
math,
systemd,
x11,
xext,
xkbconf,
Expand Down Expand Up @@ -96,6 +98,7 @@ conf.set('ENABLE_NLS', cc.has_header('libintl.h'))
conf.set('HAVE_ALSA', use_alsa)
conf.set('HAVE_GETTEXT', true)
conf.set('HAVE_INTROSPECTION', true)
conf.set('HAVE_SYSTEMD', systemd.found())
conf.set('HAVE_TIMERFD', timerfd_check)

################################################################################
Expand Down Expand Up @@ -135,5 +138,6 @@ message('\n'.join([
' Use *_DISABLE_DEPRECATED: @0@'.format(get_option('deprecation_warnings')),
' Use PNP files: ' + pnp_message,
' Use ALSA: ' + '@0@'.format(use_alsa),
' systemd: @0@'.format(systemd.found()),
'',
]))
3 changes: 3 additions & 0 deletions meson_options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ option('pnp_ids', type: 'string', value: '',
description: 'Specify the path to pnp.ids (default is an internal copy)')
option('alsa', type: 'boolean', value: false,
description: 'ALSA support')
option('systemd',
type: 'feature', description: 'Systemd integration support'
)

0 comments on commit 7eadfb1

Please sign in to comment.