Skip to content

server: Check visibility when creating/removing globals

Ghost User requested to merge (removed):dynamic-filters into main

I'm experimenting with a permission mechanism within a compositor and the handling of global filtering is a bit inconsistent -- if a client connects and binds a registry, and then the server adds a global afterwards, the client will always get a wl_registry.global message for it even if binding that global would result in failure. The first commit should fix this, by checking for visibility before sending the global and global_remove events.

The other issue I ran into is the reverse situation, during adding and removing permissions from clients. If a client starts out as being "unprivileged" and then later gets granted access to some globals that already exist, the compositor needs to manually send that client a global event (and similarly a global_remove event if the access gets revoked). But currently this is not possible because it needs the global's name, so I added wl_global_get_name. Since this requires looping through all the client's resources, I added wl_resource_get_interface to allow for fast pointer comparisons with the built-in libwayland interfaces. This should be safe since wl_interface_equal does it anyway.

Altogether, it should allow for writing a grant_access() and revoke_access() API in the compositor, and a global filter that looks something like this:

/* permission_table is essentially a Set<Tuple<wl_global, wl_client>>,
   with simple get/set functions. Implementation omitted here. */

void global_grant_access(struct permission_table *perms,
                         struct wl_global *global,
                         struct wl_client *client) {
  if (!perms_check_access(perms, global, client)) {
    perms_set_access(perms, global, client, true);
    wl_client_for_each_resource (client, post_global_to_registry, global);
  }
}

static enum wl_iterator_result post_global_to_registry(struct wl_resource *resource,
                                                       void *user_data) {
  struct wl_global *global = user_data;
  if (wl_resource_get_interface(resource) == &wl_registry_interface) {
    const struct wl_interface *interface = wl_global_get_interface(global);
    wl_registry_send_global(resource,
                            wl_global_get_name(global),
                            interface->name,
                            interface->version);
  }
  return WL_ITERATOR_CONTINUE;
}

/* later... */

struct wl_display *create_display(struct permission_table *perms) {
  struct wl_display *display = wl_display_create();
  wl_display_set_global_filter (display, access_filter, perms);
  return display;
}

static bool
access_filter (const struct wl_client *client,
               const struct wl_global *global,
               void *user_data)
{
  struct permission_table *perms = user_data;
  if (global_is_privileged(global))
    return perms_check_access(perms, global, client);
  return true;
}

If this looks good I can try to add unit tests for it before merging.

Edited by Ghost User

Merge request reports