Skip to content

Instantly share code, notes, and snippets.

@3v1n0
Created October 11, 2018 02:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 3v1n0/3e149eb7e21f166ccee1700f313ce1dc to your computer and use it in GitHub Desktop.
Save 3v1n0/3e149eb7e21f166ccee1700f313ce1dc to your computer and use it in GitHub Desktop.
mutter based GPU presency init checks
// gcc `pkg-config --cflags --libs gio-2.0 glib-2.0 gudev-1.0 libsystemd` /tmp/gpupaths.c -o /tmp/gpupaths &&/tmp/gpupaths
#include <errno.h>
#include <fcntl.h>
#include <malloc.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <systemd/sd-login.h>
#include <glib.h>
#include <gio/gio.h>
#include <gudev/gudev.h>
#define DRM_CARD_UDEV_DEVICE_TYPE "drm_minor"
static gboolean
find_systemd_session (gchar **session_id,
GError **error)
{
const gchar * const graphical_session_types[] = { "wayland", "x11", "mir", NULL };
const gchar * const active_states[] = { "active", "online", NULL };
g_autofree gchar *class = NULL;
g_autofree gchar *local_session_id = NULL;
g_autofree gchar *type = NULL;
g_autofree gchar *state = NULL;
g_auto (GStrv) sessions = NULL;
int n_sessions;
int saved_errno;
g_assert (session_id != NULL);
g_assert (error == NULL || *error == NULL);
/* if we are in a logind session, we can trust that value, so use it. This
* happens for example when you run mutter directly from a VT but when
* systemd starts us we will not be in a logind session. */
saved_errno = sd_pid_get_session (0, &local_session_id);
if (saved_errno < 0)
{
if (saved_errno != -ENODATA)
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_NOT_FOUND,
"Failed to get session by pid for user %d (%s)",
getuid (),
g_strerror (-saved_errno));
return FALSE;
}
}
else
{
*session_id = g_steal_pointer (&local_session_id);
return TRUE;
}
saved_errno = sd_uid_get_display (getuid (), &local_session_id);
if (saved_errno < 0)
{
/* no session, maybe there's a greeter session */
if (saved_errno == -ENODATA)
{
n_sessions = sd_uid_get_sessions (getuid (), 1, &sessions);
if (n_sessions < 0)
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_NOT_FOUND,
"Failed to get all sessions for user %d (%m)",
getuid ());
return FALSE;
}
if (n_sessions == 0)
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_NOT_FOUND,
"User %d has no sessions",
getuid ());
return FALSE;
}
for (int i = 0; i < n_sessions; ++i)
{
saved_errno = sd_session_get_class (sessions[i], &class);
if (saved_errno < 0)
{
g_warning ("Couldn't get class for session '%d': %s",
i,
g_strerror (-saved_errno));
continue;
}
if (g_strcmp0 (class, "greeter") == 0)
{
local_session_id = g_strdup (sessions[i]);
break;
}
}
if (!local_session_id)
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_NOT_FOUND,
"Couldn't find a session or a greeter session for user %d",
getuid ());
return FALSE;
}
}
else
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_NOT_FOUND,
"Couldn't get display for user %d: %s",
getuid (),
g_strerror (-saved_errno));
return FALSE;
}
}
/* sd_uid_get_display will return any session if there is no graphical
* one, so let's check it really is graphical. */
saved_errno = sd_session_get_type (local_session_id, &type);
if (saved_errno < 0)
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_NOT_FOUND,
"Couldn't get type for session '%s': %s",
local_session_id,
g_strerror (-saved_errno));
return FALSE;
}
if (!g_strv_contains (graphical_session_types, type))
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_NOT_FOUND,
"Session '%s' is not a graphical session (type: '%s')",
local_session_id,
type);
return FALSE;
}
/* and display sessions can be 'closing' if they are logged out but
* some processes are lingering; we shouldn't consider these */
saved_errno = sd_session_get_state (local_session_id, &state);
if (saved_errno < 0)
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_NOT_FOUND,
"Couldn't get state for session '%s': %s",
local_session_id,
g_strerror (-saved_errno));
return FALSE;
}
if (!g_strv_contains (active_states, state))
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_NOT_FOUND,
"Session '%s' is not active",
local_session_id);
return FALSE;
}
*session_id = g_steal_pointer (&local_session_id);
return TRUE;
}
/* Stolen from tp_escape_as_identifier, from tp-glib,
* which follows the same escaping convention as systemd.
*/
static inline gboolean
_esc_ident_bad (gchar c, gboolean is_first)
{
return ((c < 'a' || c > 'z') &&
(c < 'A' || c > 'Z') &&
(c < '0' || c > '9' || is_first));
}
static gchar *
escape_dbus_component (const gchar *name)
{
gboolean bad = FALSE;
size_t len = 0;
GString *op;
const gchar *ptr, *first_ok;
g_return_val_if_fail (name != NULL, NULL);
/* fast path for empty name */
if (name[0] == '\0')
return g_strdup ("_");
for (ptr = name; *ptr; ptr++)
{
if (_esc_ident_bad (*ptr, ptr == name))
{
bad = TRUE;
len += 3;
}
else
len++;
}
/* fast path if it's clean */
if (!bad)
return g_strdup (name);
/* If strictly less than ptr, first_ok is the first uncopied safe character.
*/
first_ok = name;
op = g_string_sized_new (len);
for (ptr = name; *ptr; ptr++)
{
if (_esc_ident_bad (*ptr, ptr == name))
{
/* copy preceding safe characters if any */
if (first_ok < ptr)
{
g_string_append_len (op, first_ok, ptr - first_ok);
}
/* escape the unsafe character */
g_string_append_printf (op, "_%02x", (unsigned char)(*ptr));
/* restart after it */
first_ok = ptr + 1;
}
}
/* copy trailing safe characters if any */
if (first_ok < ptr)
{
g_string_append_len (op, first_ok, ptr - first_ok);
}
return g_string_free (op, FALSE);
}
char *
get_escaped_dbus_path (const char *prefix,
const char *component)
{
char *escaped_component = escape_dbus_component (component);
char *path = g_strconcat (prefix, "/", escaped_component, NULL);
g_free (escaped_component);
return path;
}
static GVariant *
call_logind1_method(const gchar *method_name,
GVariant *parameters)
{
g_autoptr (GError) error = NULL;
g_autofree char *session_id = NULL;
GVariant *res;
if (!find_systemd_session (&session_id, &error))
{
g_printerr("Could not get session ID: %s\n", error->message);
return NULL;
}
g_autofree char *proxy_path = get_escaped_dbus_path ("/org/freedesktop/login1/session", session_id);
res = g_dbus_connection_call_sync (g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL),
"org.freedesktop.login1",
proxy_path,
"org.freedesktop.login1.Session",
method_name,
parameters,
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
if (error)
g_printerr("Impossible to call %s: %s\n",
method_name, error->message);
else
g_print("Logind1 method %s called successifully: %s\n", method_name,
g_variant_print(res, FALSE));
return res;
}
char *
get_seat_id (void)
{
g_autoptr (GError) error = NULL;
g_autofree char *session_id = NULL;
char *seat_id = NULL;
int r;
if (!find_systemd_session (&session_id, &error))
{
g_critical("Could not get session ID: %s", error->message);
return NULL;
}
g_print("Session id is %s\n",session_id);
r = sd_session_get_seat (session_id, &seat_id);
if (r < 0)
{
g_critical("Could not get seat for session: %s", g_strerror (-r));
return NULL;
}
g_print("Seat is %s\n",seat_id);
return seat_id;
}
static GList *
get_gpu_paths (gboolean primary,
const char *filtered_gpu_path)
{
g_autoptr (GUdevEnumerator) enumerator = NULL;
const char *seat_id;
GList *devices;
GList *l;
GList *gpu_paths = NULL;
const char *subsystems[2] = {"drm", NULL};
enumerator = g_udev_enumerator_new (g_udev_client_new(subsystems));
g_udev_enumerator_add_match_name (enumerator, "card*");
g_udev_enumerator_add_match_tag (enumerator, "seat");
/*
* We need to explicitly match the subsystem for now.
* https://bugzilla.gnome.org/show_bug.cgi?id=773224
*/
g_udev_enumerator_add_match_subsystem (enumerator, "drm");
devices = g_udev_enumerator_execute (enumerator);
g_print("Enumerator Executed, devices are %p, size %u\n",
devices, g_list_length(devices));
if (!devices)
return NULL;
seat_id = get_seat_id();
g_print("Seat id is %s\n",seat_id);
for (l = devices; l; l = l->next)
{
GUdevDevice *dev = l->data;
g_autoptr (GUdevDevice) platform_device = NULL;
g_autoptr (GUdevDevice) pci_device = NULL;
const char *device_path;
const char *device_type;
const char *device_seat;
device_type = g_udev_device_get_property (dev, "DEVTYPE");
device_path = g_udev_device_get_device_file (dev);
device_seat = g_udev_device_get_property (dev, "ID_SEAT");
pci_device = g_udev_device_get_parent_with_subsystem(dev, "pci", NULL);
g_print(" Checking device %s (%s | %s - type %u, devtype %s, seat %s) | pci %d\n",
g_udev_device_get_name(dev),
g_udev_device_get_sysfs_path(dev), device_path,
g_udev_device_get_device_type(dev),
device_type, device_seat, (pci_device != NULL));
/* Filter out devices that are not character device, like card0-VGA-1. */
if (g_udev_device_get_device_type (dev) != G_UDEV_DEVICE_TYPE_CHAR)
continue;
if (g_strcmp0 (device_type, DRM_CARD_UDEV_DEVICE_TYPE) != 0)
continue;
if (g_strcmp0 (device_path, filtered_gpu_path) == 0)
continue;
if (!device_seat)
{
/* When ID_SEAT is not set, it means seat0. */
device_seat = "seat0";
}
else if (g_strcmp0 (device_seat, "seat0") != 0 && primary)
{
/*
* If the device has been explicitly assigned other seat
* than seat0, it is probably the right device to use.
*/
gpu_paths = g_list_append (gpu_paths, g_strdup (device_path));
break;
}
/* Skip devices that do not belong to our seat. */
if (g_strcmp0 (seat_id, device_seat))
continue;
platform_device = g_udev_device_get_parent_with_subsystem (dev,
"platform",
NULL);
if (platform_device != NULL && primary)
{
g_print("Returning platform device\n");
gpu_paths = g_list_append (gpu_paths, g_strdup (device_path));
break;
}
if (pci_device != NULL)
{
if (primary)
{
int boot_vga;
boot_vga = g_udev_device_get_sysfs_attr_as_int (pci_device,
"boot_vga");
g_print(" boot_vga %d\n", boot_vga);
if (boot_vga == 1)
{
gpu_paths = g_list_append (gpu_paths, g_strdup (device_path));
break;
}
}
else
{
gpu_paths = g_list_append (gpu_paths, g_strdup (device_path));
}
}
}
g_list_free_full (devices, g_object_unref);
return gpu_paths;
}
int main(void)
{
GList* gpus, *l;
call_logind1_method("TakeControl", g_variant_new("(b)", FALSE));
gpus = get_gpu_paths(TRUE, NULL);
call_logind1_method("ReleaseControl", NULL);
g_print("Seat info");
system("/bin/loginctl show-seat seat0");
g_print("Drm cards are (via ls)\n");
system("ls -lR /dev/dri/");
g_print("=============\n");
g_print("Found GPUs %p, size %u\n",gpus, g_list_length(gpus));
for (l = gpus; l; l = l->next)
g_print("GPU: %s\n", (const char*) l->data);
g_list_free (gpus);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment