Created
October 11, 2018 02:40
-
-
Save 3v1n0/3e149eb7e21f166ccee1700f313ce1dc to your computer and use it in GitHub Desktop.
mutter based GPU presency init checks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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