From 8fc654bfb09157e746efdae36d654258e7e8102f Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 8 Sep 2022 09:13:56 +1000 Subject: [PATCH] Purge the portal code The original idea here was that we would have an EmulatedInput portal that allows the application to connect directly to the EIS implementation to exchange input events - instead of ping-ponging DBus events through the xdg-desktop-portal as the RemoteDesktop portal requires. This is no longer accurate, there are suggested PRs open to add RemoteDesktop.ConnectToEIS to achieve the same through the existing RemoteDesktop interface [1] and to add a new InputCapture portal to allow for events to be sent to a libei receiver context [2]. The example EmulatedInput portal is thus superfluous and can be removed from here. We could switch the ei_setup_backend_portal() code to use RemoteDesktop or InputCapture, depending on the context type, the utility of this is questionable. Interaction with portals is complex, one needs to implement the Session/Request interfaces correctly and in the case of InputCapture also handle the complex zones/pointer barrier setup. libportal does some of this (or it will, anyway) so it's more useful for an application to use libportal and then just pass the received fd to libei. If there is a future need for this to be handled as part of libei, we can (re)implement this, but for now it's best to just purge all of this. [1] https://github.com/flatpak/xdg-desktop-portal/pull/762 [2] https://github.com/flatpak/xdg-desktop-portal/pull/714 --- .gitlab-ci.yml | 8 - .gitlab-ci/ci.template | 8 - doc/mainpage.dox | 4 - examples/libei-client.pseudo | 2 +- examples/libeis-server.pseudo | 2 +- meson.build | 25 -- meson_options.txt | 1 - ....freedesktop.impl.portal.EmulatedInput.xml | 92 ---- .../org.freedesktop.portal.EmulatedInput.xml | 97 ---- src/libei-portal.c | 357 --------------- src/libei-stubs.c | 39 -- src/libei.h | 20 - tools/ei-debug-events.c | 3 +- tools/ei-demo-client.c | 3 +- tools/eis-demo-server.c | 3 +- tools/eis-fake-impl-portal.c | 377 ---------------- tools/eis-fake-portal.c | 417 ------------------ 17 files changed, 5 insertions(+), 1453 deletions(-) delete mode 100644 portal/org.freedesktop.impl.portal.EmulatedInput.xml delete mode 100644 portal/org.freedesktop.portal.EmulatedInput.xml delete mode 100644 src/libei-portal.c delete mode 100644 src/libei-stubs.c delete mode 100644 tools/eis-fake-impl-portal.c delete mode 100644 tools/eis-fake-portal.c diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 13fe748..c1682cd 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -254,14 +254,6 @@ build-no-doxygen@fedora:37: before_script: - dnf remove -y doxygen -build-no-portal@fedora:37: - extends: - - .fedora-build@template - before_script: - - dnf remove -y libsystemd-devel - variables: - MESON_ARGS: "-Dportal=false" - valgrind@fedora:37: extends: - .fedora-build@template diff --git a/.gitlab-ci/ci.template b/.gitlab-ci/ci.template index b420260..ae3fb8a 100644 --- a/.gitlab-ci/ci.template +++ b/.gitlab-ci/ci.template @@ -275,14 +275,6 @@ build-no-doxygen@{{distro.name}}:{{version}}: before_script: - dnf remove -y doxygen -build-no-portal@{{distro.name}}:{{version}}: - extends: - - .{{distro.name}}-build@template - before_script: - - dnf remove -y libsystemd-devel - variables: - MESON_ARGS: "-Dportal=false" - valgrind@{{distro.name}}:{{version}}: extends: - .{{distro.name}}-build@template diff --git a/doc/mainpage.dox b/doc/mainpage.dox index b183d9f..435500c 100644 --- a/doc/mainpage.dox +++ b/doc/mainpage.dox @@ -58,10 +58,6 @@ implementation and sends events. The `eis-demo-server` is a minimal EIS implementation that accepts all requests and prints them to screen. -The `eis-fake-portal` is a minimal [XDG Desktop -Portal](https://github.com/flatpak/xdg-desktop-portal/) implementation that -connects a portal-aware libei client with an EIS implementation. - @section building_against Building against libei or libeis libei and libeis provides diff --git a/examples/libei-client.pseudo b/examples/libei-client.pseudo index 5fc2d38..dbc7509 100644 --- a/examples/libei-client.pseudo +++ b/examples/libei-client.pseudo @@ -2,7 +2,7 @@ function main(): ctx = ei_new() - ei_portal_connect(ctx) + ei_connect(ctx) ei_dispatch(ctx) # let's say this is a blocking wait diff --git a/examples/libeis-server.pseudo b/examples/libeis-server.pseudo index 4a36d77..42b7040 100644 --- a/examples/libeis-server.pseudo +++ b/examples/libeis-server.pseudo @@ -7,7 +7,7 @@ function main(): ctx = eis_new() - eis_portal_init(ctx) + eis_init(ctx) while True: poll(eis_get_fd()): diff --git a/meson.build b/meson.build index 70d885c..ec53ed9 100644 --- a/meson.build +++ b/meson.build @@ -94,25 +94,12 @@ src_libei = [ 'src/libei-proto.h', 'src/libei-proto.c', 'src/libei-region.c', - 'src/libei-stubs.c', proto_headers, ] - -if get_option('portal') - dep_systemd = dependency('libsystemd') - config_h.set10('ENABLE_LIBEI_PORTAL', true) - src_libei += [ - 'src/libei-portal.c', - ] -else - dep_systemd = dependency('', required: false) -endif - deps_libei = [ dep_libutil, dep_protobuf, - dep_systemd, ] lib_libei = shared_library('ei', @@ -133,7 +120,6 @@ pkgconfig.generate(lib_libei, version: meson.project_version(), libraries: lib_libei, variables: [ - 'portal=' + get_option('portal').to_string(), 'protocol_version=' + protocol_version.to_string(), ], ) @@ -238,17 +224,6 @@ executable('ei-debug-events', dependencies: [dep_libutil, dep_libei, dep_libevdev], install: true) -if get_option('portal') - executable('eis-fake-portal', - 'tools/eis-fake-portal.c', - include_directories: 'src', - dependencies: [dep_libutil, dep_systemd, dep_libreis]) - executable('eis-fake-impl-portal', - 'tools/eis-fake-impl-portal.c', - include_directories: 'src', - dependencies: [dep_libutil, dep_systemd, dep_libreis]) -endif - # tests if get_option('tests') subproject('munit', default_options: 'werror=false') diff --git a/meson_options.txt b/meson_options.txt index 0b426c3..a2862fc 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,3 +1,2 @@ -option('portal', type: 'boolean', value: 'true', description: 'Enable/disable org.freedesktop.portal support') option('documentation', type: 'boolean', value: 'false', description: 'Enable/disable building the API documentation') option('tests', type: 'boolean', value: 'true', description: 'Enable/disable tests') diff --git a/portal/org.freedesktop.impl.portal.EmulatedInput.xml b/portal/org.freedesktop.impl.portal.EmulatedInput.xml deleted file mode 100644 index d2daba3..0000000 --- a/portal/org.freedesktop.impl.portal.EmulatedInput.xml +++ /dev/null @@ -1,92 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/portal/org.freedesktop.portal.EmulatedInput.xml b/portal/org.freedesktop.portal.EmulatedInput.xml deleted file mode 100644 index d57cb59..0000000 --- a/portal/org.freedesktop.portal.EmulatedInput.xml +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/libei-portal.c b/src/libei-portal.c deleted file mode 100644 index 096225d..0000000 --- a/src/libei-portal.c +++ /dev/null @@ -1,357 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* - * Copyright © 2020 Red Hat, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include "config.h" - -#include -#include -#include -#include - -#include "libei.h" -#include "libei-private.h" - -#include "util-io.h" -#include "util-macros.h" -#include "util-mem.h" -#include "util-object.h" -#include "util-strings.h" - -struct ei_portal { - struct object object; - struct source *bus_source; - sd_bus *bus; - sd_bus_slot *slot; - - char *busname; -}; - -static void -ei_portal_destroy(struct ei_portal *portal) -{ - free(portal->busname); - sd_bus_unref(portal->bus); -} - -static -OBJECT_IMPLEMENT_CREATE(ei_portal); -static -OBJECT_IMPLEMENT_PARENT(ei_portal, ei); -static -OBJECT_IMPLEMENT_REF(ei_portal); -static -OBJECT_IMPLEMENT_UNREF_CLEANUP(ei_portal); - -static void -interface_portal_destroy(struct ei *ei, void *backend) -{ - struct ei_portal *portal = backend; - ei_portal_unref(portal); -} - -static const struct ei_backend_interface interface = { - .destroy = interface_portal_destroy, -}; - -static char * -xdp_token(sd_bus *bus, char **token_out) -{ - _cleanup_free_ char *sender = NULL; - const char *name = NULL; - - if (sd_bus_get_unique_name(bus, &name) != 0) - return NULL; - - sender = xstrdup(name + 1); /* drop initial : */ - - for (unsigned i = 0; sender[i]; i++) { - if (sender[i] == '.') - sender[i] = '_'; - } - - char *token = xaprintf("ei_%d", rand()); - *token_out = token; - - return xaprintf("/org/freedesktop/portal/desktop/request/%s/%s", sender, token); -} - -static void -portal_connect(struct ei_portal *portal, const char *session_handle) -{ - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _unref_(sd_bus_message) *response = NULL; - struct sd_bus *bus = portal->bus; - struct ei *ei = ei_portal_parent(portal); - int eisfd; - - int rc = sd_bus_call_method(bus, portal->busname, - "/org/freedesktop/portal/desktop", - "org.freedesktop.portal.EmulatedInput", - "ConnectToEIS", - &error, - &response, - "oa{sv}", - session_handle, - 0); - - if (rc < 0) { - log_error(ei, "Failed to call method: %s", strerror(-rc)); - goto out; - } - - int status; - rc = sd_bus_message_read(response, "u", &status); - if (rc < 0) { - log_error(ei, "Failed to extract status, invalid message format: %s", strerror(-rc)); - goto out; - } - - if (status != 0) { - log_info(ei, "Unable to get fd from portal"); - ei_disconnect(ei); - return; - } - - const char *key; - rc = sd_bus_message_read(response, "a{sv}", 1, &key, "h", &eisfd); - if (rc < 0) { - log_error(ei, "Failed to extract fd, invalid message format: %s", strerror(-rc)); - goto out; - } - - if (!streq(key, "fd")) { - log_error(ei, "Invalid key '%s', expected 'fd'", key); - goto out; - } - - /* the fd is owned by the message */ - rc = xerrno(dup(eisfd)); - if (rc < 0) { - log_error(ei, "Failed to dup fd: %s", strerror(-rc)); - goto out; - } else { - eisfd = rc; - int flags = fcntl(eisfd, F_GETFL, 0); - fcntl(eisfd, F_SETFL, flags | O_NONBLOCK); - } - - log_debug(ei, "Initiating ei context with fd %d from portal", eisfd); - - /* We're done with DBus, lets clean up */ - source_remove(portal->bus_source); - source_unref(portal->bus_source); - portal->bus = sd_bus_unref(portal->bus); - - rc = ei_set_connection(ei, eisfd); -out: - if (rc < 0) { - log_error(ei, "Failed to set the connection: %s", strerror(-rc)); - ei_disconnect(ei); - } -} - -static int -portal_response_received(sd_bus_message *m, void *userdata, sd_bus_error *error) -{ - struct ei_portal *portal = userdata; - struct ei *ei = ei_portal_parent(portal); - unsigned response; - - assert(m); - assert(portal); - - /* We'll only get this signal once */ - portal->slot = sd_bus_slot_unref(portal->slot); - - int rc = sd_bus_message_read(m, "u", &response); - if (rc < 0) { - log_error(ei, "Failed to read response from signal: %s", strerror(-rc)); - ei_disconnect(ei); - return 0; - } - - log_debug(ei, "Portal CreateSession reponse is %u", response); - - const char *session_handle = NULL; - if (response == 0) { - const char *key; - rc = sd_bus_message_read(m, "a{sv}", 1, &key, "s", &session_handle); - if (rc < 0) { - log_error(ei, "Failed to read session handle from signal: %s", strerror(-rc)); - ei_disconnect(ei); - return 0; - } - - if (!streq(key, "session_handle")) { - log_error(ei, "Invalid or unhandled option: %ss", key); - ei_disconnect(ei); - return 0; - } - } - - /* FIXME: we need to subscribe to the sessions Close signal */ - - if (response != 0) { - ei_disconnect(ei); - return 0; - } - - portal_connect(portal, session_handle); - - return 0; -} - -static int -session_closed_received(sd_bus_message *m, void *userdata, sd_bus_error *error) -{ - struct ei_portal *portal = userdata; - struct ei *ei = ei_portal_parent(portal); - - log_error(ei, "Session closed received"); - return 0; -} - -static void -dbus_dispatch(struct source *source, void *data) -{ - struct ei_portal *portal = data; - - /* We need to ref the bus here, portal_connect() may remove - * portal->bus but that needs to stay valid here until the end of - * the loop. - */ - _unref_(sd_bus) *bus = sd_bus_ref(portal->bus); - - int rc; - do { - rc = sd_bus_process(bus, NULL); - } while (rc > 0); - - if (rc != 0) { - log_error(ei_portal_parent(portal), "dbus processing failed with %s", strerror(-rc)); - } -} - -static int -portal_init(struct ei *ei, const char *busname) -{ - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _unref_(sd_bus_message) *response = NULL; - _unref_(sd_bus) *bus = NULL; - _unref_(sd_bus_slot) *slot = NULL; - _unref_(ei_portal) *portal = ei_portal_create(&ei->object); - const char *path = NULL; - - int rc = sd_bus_open_user(&bus); - if (rc < 0) { - log_error(ei, "Failed to init dbus: %s", strerror(-rc)); - return -ECONNREFUSED; - } - - _cleanup_free_ char *token = NULL; - _cleanup_free_ char *handle = xdp_token(bus, &token); - _cleanup_free_ char *session_token = xdp_token(bus, &session_token); - _cleanup_free_ char *session_handle = xdp_token(bus, &session_token); - - rc = sd_bus_match_signal(bus, &slot, - busname, - handle, - "org.freedesktop.portal.Request", - "Response", - portal_response_received, - portal); - if (rc < 0) { - log_error(ei, "Failed to subscribe to signal: %s", strerror(-rc)); - return -ECONNREFUSED; - } - - rc = sd_bus_match_signal(bus, &slot, - busname, - session_handle, - "org.freedesktop.portal.Session", - "Closed", - session_closed_received, - portal); - if (rc < 0) { - log_error(ei, "Failed to subscribe to signal: %s", strerror(-rc)); - return -ECONNREFUSED; - } - - rc = sd_bus_call_method(bus, - busname, - "/org/freedesktop/portal/desktop", - "org.freedesktop.portal.EmulatedInput", - "CreateSession", - &error, - &response, - "a{sv}", 2, - "handle_token", /* string key */ - "s", token, /* variant string */ - "session_handle_token", /* string key */ - "s", session_token /* variant string */ - ); - - if (rc < 0) { - log_error(ei, "Failed to call method: %s", strerror(-rc)); - return -ECONNREFUSED; - } - - rc = sd_bus_message_read(response, "o", &path); - if (rc < 0) { - log_error(ei, "Failed to parse response: %s", strerror(-rc)); - return -ECONNREFUSED; - } - - log_debug(ei, "portal Response object is %s", path); - - struct source *s = source_new(sd_bus_get_fd(bus), dbus_dispatch, portal); - source_never_close_fd(s); /* the bus object handles the fd */ - rc = sink_add_source(ei->sink, s); - if (rc == 0) { - portal->bus_source = source_ref(s); - portal->bus = sd_bus_ref(bus); - portal->slot = sd_bus_slot_ref(slot); - } - - portal->busname = xstrdup(busname); - - ei->backend = ei_portal_ref(portal); - ei->backend_interface = interface; - - source_unref(s); - - return 0; -} - -_public_ int -ei_setup_backend_portal(struct ei *ei) -{ - return portal_init(ei, "org.freedesktop.portal.Desktop"); -} - -_public_ int -ei_setup_backend_portal_busname(struct ei *ei, const char *busname) -{ - return portal_init(ei, busname); -} diff --git a/src/libei-stubs.c b/src/libei-stubs.c deleted file mode 100644 index 7c934bd..0000000 --- a/src/libei-stubs.c +++ /dev/null @@ -1,39 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* - * Copyright © 2020 Red Hat, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include "config.h" - -#include - -#include "libei.h" - -#include "util-macros.h" - -#if !ENABLE_LIBEI_PORTAL -_public_ int -ei_setup_backend_portal(struct ei *ei) -{ - return -ENOSYS; -} -#endif diff --git a/src/libei.h b/src/libei.h index a9a978b..5fa45af 100644 --- a/src/libei.h +++ b/src/libei.h @@ -581,26 +581,6 @@ ei_setup_backend_socket(struct ei *ei, const char *socketpath); int ei_setup_backend_fd(struct ei *ei, int fd); -/** - * Connect to the `org.freedesktop.portal.Desktop` portal. - * - * @return 0 on success or a negative errno on failure - */ -int -ei_setup_backend_portal(struct ei *ei); - -/** - * Connect to an `org.freedesktop.portal.Desktop` implementation on the - * given busname. - * - * Outside of testing environments, there is usually no reason to use - * this function, use ei_setup_backend_portal() instead. - * - * @return 0 on success or a negative errno on failure - */ -int -ei_setup_backend_portal_busname(struct ei *ei, const char *busname); - /** * Increase the refcount of this struct by one. Use ei_unref() to decrease * the refcount. diff --git a/tools/ei-debug-events.c b/tools/ei-debug-events.c index 311f16e..69c5d5b 100644 --- a/tools/ei-debug-events.c +++ b/tools/ei-debug-events.c @@ -30,8 +30,7 @@ * * Usually, you'd want to: * - run the eis-demo-server (or some other EIS implementation) - * - run the eis-fake-portal (if testing portal clients), otherwise - * export LIBEI_SOCKET=eis-0, or whatever value was given. + * - export LIBEI_SOCKET=eis-0, or whatever value was given * - run the ei-demo-client */ diff --git a/tools/ei-demo-client.c b/tools/ei-demo-client.c index c02a622..88e4fc8 100644 --- a/tools/ei-demo-client.c +++ b/tools/ei-demo-client.c @@ -30,8 +30,7 @@ * * Usually, you'd want to: * - run the eis-demo-server (or some other EIS implementation) - * - run the eis-fake-portal (if testing portal clients), otherwise - * export LIBEI_SOCKET=eis-0, or whatever value was given. + * - export LIBEI_SOCKET=eis-0, or whatever value was given * - run the ei-demo-client */ diff --git a/tools/eis-demo-server.c b/tools/eis-demo-server.c index cee0e71..2279f65 100644 --- a/tools/eis-demo-server.c +++ b/tools/eis-demo-server.c @@ -33,8 +33,7 @@ * * Usually, you'd want to: * - run the eis-demo-server (or some other EIS implementation) - * - run the eis-fake-portal (if testing portal clients), otherwise - * export LIBEI_SOCKET=eis-0, or whatever value was given. + * - export LIBEI_SOCKET=eis-0, or whatever value was given * - run the libei client */ diff --git a/tools/eis-fake-impl-portal.c b/tools/eis-fake-impl-portal.c deleted file mode 100644 index 9843e13..0000000 --- a/tools/eis-fake-impl-portal.c +++ /dev/null @@ -1,377 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* - * Copyright © 2021 Red Hat, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -/* A simple tool that sets up the org.freedesktop.impl.portal.EmulatedInput - * DBus interface and provides the required functionality. - * - * This tool is useful for testing portals (e.g. - * xdg-desktop-portal) that need to connect to impl.portal. It provides - * enough data to succeed with the connection. - * - * This tool does not run an EIS server, use e.g. the eis-demo-server. - * - * Usually, you'd want to: - * - run the eis-demo-server (or some other EIS implementation) - * - run the eis-fake-impl-portal - * - run xdg-desktop-portal - * - run the libei client relying on the portal - */ - -#include "config.h" - -#include -#include -#include -#include -#include - -#include "util-io.h" -#include "util-list.h" -#include "util-mem.h" -#include "util-logger.h" -#include "util-strings.h" - -#include "libreis.h" - -#include - -DEFINE_UNREF_CLEANUP_FUNC(reis); - -struct session { - unsigned int version; - struct list link; - struct sd_bus_slot *slot; - char *handle; -}; - -struct portal { - unsigned int version; - struct logger *logger; - char *busname; - - struct list sessions; -} portal; - - -#define call(_call) do { \ - int _rc = _call; \ - if (_rc < 0) { \ - log_error(portal, "Failed with %s %s:%d\n", strerror(-_rc), __func__, __LINE__); \ - return _rc; \ - } } while(0) - -static int -session_close(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) -{ - sd_bus_reply_method_return(m, ""); - /* Not implemented */ - return 0; -} - -static const sd_bus_vtable session_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_METHOD("Close", "", "", session_close, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_PROPERTY("version", "u", NULL, offsetof(struct session, version), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_VTABLE_END, -}; - -static int -create_session_object(struct portal *portal, - sd_bus *bus, - const char *objectpath) -{ - struct session *session = calloc(sizeof *session, 1); - - session->handle = xstrdup(objectpath); - - call(sd_bus_add_object_vtable(bus, &session->slot, - objectpath, - "org.freedesktop.impl.portal.Session", - session_vtable, - session)); - - list_append(&portal->sessions, &session->link); - - log_debug(portal, "Session created on %s\n", objectpath); - return 0; -} - -static int -request_close(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) -{ - /* We don't live long enough for this to be called */ - return 0; -} - -static const sd_bus_vtable request_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_METHOD("Close", "", "", request_close, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_VTABLE_END, -}; - -static int -create_request_object(struct portal *portal, - sd_bus *bus, - const char *objectpath, - const char *session_path) -{ - _unref_(sd_bus_slot) *slot = NULL; - - create_session_object(portal, bus, session_path); - - call(sd_bus_add_object_vtable(bus, &slot, - objectpath, - "org.freedesktop.impl.portal.Request", - request_vtable, - NULL)); - - int response = 0; - log_debug(portal, "emitting Response %d on %s\n", response, objectpath); - return sd_bus_emit_signal(bus, - objectpath, - "org.freedesktop.impl.portal.Request", - "Response", - "ua{sv}", - response, - 1, - "session_handle",/* string key */ - "s", session_path /* variant string */ - ); - /* note: _unref_ removes object immediately */ -} - -static int -portal_create_session(sd_bus_message *m, void *userdata, - sd_bus_error *ret_error) -{ - struct portal *portal = userdata; - static int session_id = 1234; - - const char *handle; - const char *session_handle; - const char *app_id; - - sd_bus_message_read(m, "oosa{sv}", - &handle, - &session_handle, - &app_id, 0); - - log_debug(portal, "CreateSession: app-id %s, session %s, handle %s\n", app_id, session_handle, handle); - - _cleanup_free_ char *sid = xaprintf("%d", session_id++); - - call(sd_bus_reply_method_return(m, "ua{sv}", 0, 1, "session_id", "s", sid)); - - return create_request_object(portal, sd_bus_message_get_bus(m), handle, session_handle); -} - -static void -set_prop_cmdline(struct reis *reis) -{ - _cleanup_free_ char *cmdline = cmdline_as_str(); - reis_set_property_with_permissions(reis, "ei.application.cmdline", cmdline, REIS_PROPERTY_PERM_NONE); -} - -static void -set_prop_pid(struct reis *reis) -{ - char pid[64]; - - xsnprintf(pid, sizeof(pid), "%i", getpid()); - reis_set_property_with_permissions(reis, "ei.application.pid", pid, REIS_PROPERTY_PERM_NONE); -} - -static void -set_prop_type(struct reis *reis) -{ - reis_set_property_with_permissions(reis, "ei.connection.type", "portal", REIS_PROPERTY_PERM_NONE); -} - -static int -portal_connect_to_eis(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) -{ - struct portal *portal = userdata; - - log_debug(portal, "Got a connect request\n"); - - const char *xdg = getenv("XDG_RUNTIME_DIR"); - if (!xdg) - return -ENOENT; - - const char *session_handle; - const char *app_id; - call(sd_bus_message_read(m, "os", &session_handle, &app_id)); - - log_debug(portal, "Connect request session %s appid %s\n", session_handle, app_id); - - struct session *session; - bool found = false; - list_for_each_safe(session, &portal->sessions, link) { - if (streq(session->handle, session_handle)) { - found = true; - break; - } - } - - /* We're aborting here because it's a client bug and this is just a - * fake portal */ - if (!found) { - log_error(portal, "Invalid session handle %s\n", session_handle); - abort(); - } - - log_error(portal, "Valid session %s, connecting to EIS\n", session_handle); - - _cleanup_free_ char *sockpath = xaprintf("%s/eis-0", xdg); - int fd = xconnect(sockpath); - if (fd < 0) { - log_error(portal, "Failed to connect to EIS (%s)\n", strerror(-fd)); - return sd_bus_reply_method_return(m, "ua{sv}", 1, 0); - } - - _unref_(reis) *reis = reis_new(fd); - assert(reis); - set_prop_pid(reis); - set_prop_cmdline(reis); - set_prop_type(reis); - - log_debug(portal, "passing fd %d\n", fd); - return sd_bus_reply_method_return(m, "ua{sv}", 0, - 1, /* array length */ - "fd", "h", fd /* name: "fd", type handle, value fd */); -} - -static const sd_bus_vtable portal_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_METHOD("CreateSession", "oosa{sv}", "ua{sv}", portal_create_session, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ConnectToEIS", "osa{sv}", "ua{sv}", portal_connect_to_eis, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_PROPERTY("version", "u", NULL, offsetof(struct portal, version), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_VTABLE_END, -}; - -static int -run(struct portal *portal) -{ - _unref_(sd_bus) *bus = NULL; - _unref_(sd_bus_slot) *slot = NULL; - int rc = sd_bus_open_user(&bus); - if (rc < 0) - return rc; - - rc = sd_bus_add_object_vtable(bus, &slot, - "/org/freedesktop/portal/desktop", - "org.freedesktop.impl.portal.EmulatedInput", - portal_vtable, - portal); - if (rc < 0) - return rc; - - sd_bus_add_object_manager(bus, NULL, "/org/freedesktop/portal/desktop"); - - log_debug(portal, "Portal object at: /org/freedesktop/ei/EmulatedInput\n"); - - rc = sd_bus_request_name(bus, portal->busname, 0); - if (rc < 0) - return rc; - - log_debug(portal, "Portal DBus name: %s\n", portal->busname); - - _unref_(sd_event) *event = NULL; - rc = sd_event_default(&event); - if (rc < 0) - return rc; - - rc = sd_event_set_watchdog(event, true); - if (rc < 0) - return rc; - - rc = sd_bus_attach_event(bus, event, 0); - if (rc < 0) - return rc; - - return sd_event_loop(event); -} - -static void -usage(FILE *fp, const char *argv0) -{ - fprintf(fp, - "Usage: %s [--busname=a.b.c.d]\n" - "\n" - "Emulates an XDG Desktop portal for the org.freedesktop.impl.portal.EmulatedInput interface\n" - "\n" - "Options:\n" - " --busname use the given busname instead of the default org.freedesktop.impl.portal.eis.EmulatedInput\n" - "", - basename(argv0)); -} - -int -main(int argc, char **argv) -{ - _cleanup_free_ char *busname = xstrdup("org.freedesktop.impl.portal.eis.EmulatedInput"); - - while (1) { - enum opts { - OPT_BUSNAME, - }; - static struct option long_opts[] = { - { "busname", required_argument, 0, OPT_BUSNAME}, - { "help", no_argument, 0, 'h'}, - { .name = NULL }, - }; - - int optind = 0; - int c = getopt_long(argc, argv, "h", long_opts, &optind); - if (c == -1) - break; - - switch(c) { - case 'h': - usage(stdout, argv[0]); - return EXIT_SUCCESS; - case OPT_BUSNAME: - free(busname); - busname = xstrdup(optarg); - break; - default: - usage(stderr, argv[0]); - return EXIT_FAILURE; - } - } - - list_init(&portal.sessions); - portal.version = 1; - portal.busname = steal(&busname); - portal.logger = logger_new("impl.portal", NULL); - logger_set_priority(portal.logger, LOGGER_DEBUG); - - int rc = run(&portal); - if (rc < 0) - fprintf(stderr, "Failed to start fake portal: %s\n", strerror(-rc)); - - logger_unref(portal.logger); - free(portal.busname); - return rc == 0; -} diff --git a/tools/eis-fake-portal.c b/tools/eis-fake-portal.c deleted file mode 100644 index abfdf75..0000000 --- a/tools/eis-fake-portal.c +++ /dev/null @@ -1,417 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* - * Copyright © 2020 Red Hat, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -/* A simple tool that sets up the org.freedesktop.portal.EmulatedInput DBus - * interface and provides the required functionality. - * - * This tool is useful for testing the libei portal backend, it provides just - * enough data to have that backend succeed with the connection. - * - * This tool does not run an EIS server, use e.g. the eis-demo-server. - * - * Usually, you'd want to: - * - run the eis-demo-server (or some other EIS implementation) - * - run the eis-fake-portal - * - run the libei client relying on the portal - */ - -#include "config.h" - -#include -#include -#include -#include -#include - -#include "util-io.h" -#include "util-list.h" -#include "util-mem.h" -#include "util-logger.h" -#include "util-strings.h" - -#include "libreis.h" - -#include - -DEFINE_UNREF_CLEANUP_FUNC(reis); - -struct session { - struct list link; - struct sd_bus_slot *slot; - char *handle; -}; - -struct portal { - struct logger *logger; - char *busname; - - struct list sessions; -} portal; - - -#define call(_call) do { \ - int _rc = _call; \ - if (_rc < 0) { \ - log_error(portal, "Failed with %s %s:%d\n", strerror(-_rc), __func__, __LINE__); \ - return _rc; \ - } } while(0) - -static int -session_close(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) -{ - /* Not implemented */ - return 0; -} - -static const sd_bus_vtable session_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_METHOD("Close", "", "", session_close, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_SIGNAL("Closed", "ua{sv}", 0), - SD_BUS_VTABLE_END, -}; - -static int -create_session_object(struct portal *portal, - sd_bus *bus, - const char *objectpath) -{ - struct session *session = calloc(sizeof *session, 1); - - session->handle = xstrdup(objectpath); - - call(sd_bus_add_object_vtable(bus, &session->slot, - objectpath, - "org.freedesktop.portal.Session", - session_vtable, - NULL)); - - list_append(&portal->sessions, &session->link); - - log_debug(portal, "Session created on %s\n", objectpath); - return 0; -} - -static int -request_close(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) -{ - /* We don't live long enough for this to be called */ - return 0; -} - -static const sd_bus_vtable request_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_METHOD("Close", "", "", request_close, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_SIGNAL("Response", "ua{sv}", 0), - SD_BUS_VTABLE_END, -}; - -static int -create_request_object(struct portal *portal, - sd_bus *bus, - const char *objectpath, - const char *session_path) -{ - _unref_(sd_bus_slot) *slot = NULL; - - create_session_object(portal, bus, session_path); - - call(sd_bus_add_object_vtable(bus, &slot, - objectpath, - "org.freedesktop.portal.Request", - request_vtable, - NULL)); - - int response = 0; - log_debug(portal, "emitting Response %d on %s\n", response, objectpath); - return sd_bus_emit_signal(bus, - objectpath, - "org.freedesktop.portal.Request", - "Response", - "ua{sv}", - response, - 1, - "session_handle",/* string key */ - "s", session_path /* variant string */ - ); - /* note: _unref_ removes object immediately */ -} - -static char * -sender_token(const char *input) -{ - if (!input) - return NULL; - - char *token = strstrip(input, ":"); - for (size_t idx = 0; token[idx]; idx++) { - if (token[idx] == '.') - token[idx] = '_'; - } - - return token; -} - -static int -portal_create_session(sd_bus_message *m, void *userdata, - sd_bus_error *ret_error) -{ - struct portal *portal = userdata; - - call(sd_bus_message_enter_container(m, 'a', "{sv}")); - - const char *session_token = NULL; - const char *handle_token = NULL; - - const char *key, *val; - call(sd_bus_message_read(m, "{sv}", &key, "s", &val)); - if (streq(key, "handle_token")) - handle_token = val; - else if (streq(key, "session_handle_token")) - session_token = val; - else - return -EINVAL; - - call(sd_bus_message_read(m, "{sv}", &key, "s", &val)); - if (streq(key, "handle_token")) - handle_token = val; - else if (streq(key, "session_handle_token")) - session_token = val; - else - return -EINVAL; - - assert(handle_token); - assert(session_token); - - call(sd_bus_message_exit_container(m)); - - _cleanup_free_ char *sender = sender_token(sd_bus_message_get_sender(m)); - if (!sender) - return -ENOMEM; - - /* Send back the object path of the object we're about to create. We - * then create the object, so if that fails we have a problem but - * meh, this is for testing only .*/ - _cleanup_free_ char *objpath = xaprintf("%s/request/%s/%s", - "/org/freedesktop/portal/desktop", - sender, - handle_token); - call(sd_bus_reply_method_return(m, "o", objpath)); - - _cleanup_free_ char *session_path = xaprintf("%s/session/%s/%s", - "/org/freedesktop/portal/desktop", - sender, - session_token); - - /* now create the object */ - return create_request_object(portal, sd_bus_message_get_bus(m), objpath, session_path); -} - -static void -set_prop_cmdline(struct reis *reis) -{ - _cleanup_free_ char *cmdline = cmdline_as_str(); - reis_set_property_with_permissions(reis, "ei.application.cmdline", cmdline, REIS_PROPERTY_PERM_NONE); -} - -static void -set_prop_pid(struct reis *reis) -{ - char pid[64]; - - xsnprintf(pid, sizeof(pid), "%i", getpid()); - reis_set_property_with_permissions(reis, "ei.application.pid", pid, REIS_PROPERTY_PERM_NONE); -} - -static void -set_prop_type(struct reis *reis) -{ - reis_set_property_with_permissions(reis, "ei.connection.type", "portal", REIS_PROPERTY_PERM_NONE); -} - -static int -portal_connect_to_eis(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) -{ - struct portal *portal = userdata; - - const char *xdg = getenv("XDG_RUNTIME_DIR"); - if (!xdg) - return -ENOENT; - - const char *session_handle; - - call(sd_bus_message_read(m, "oa{sv}", &session_handle, 0)); - - struct session *session; - bool found = false; - list_for_each_safe(session, &portal->sessions, link) { - if (streq(session->handle, session_handle)) { - found = true; - break; - } - } - - /* We're aborting here because it's a client bug and this is just a - * fake portal */ - if (!found) { - log_error(portal, "Invalid session handle %s\n", session_handle); - return sd_bus_reply_method_return(m, "ua{sv}", 1, 0); - } - - log_info(portal, "Valid session %s, connecting to EIS\n", session_handle); - - _cleanup_free_ char *sockpath = xstrdup(getenv("LIBEI_SOCKET")); - if (!sockpath) - sockpath = xaprintf("%s/eis-0", xdg); - - int handle = xconnect(sockpath); - if (handle < 0) { - log_error(portal, "Failed to connect to EIS (%s)\n", strerror(-handle)); - return sd_bus_reply_method_return(m, "ua{sv}", 1, 0); - } - - _unref_(reis) *reis = reis_new(handle); - assert(reis); - set_prop_pid(reis); - set_prop_cmdline(reis); - set_prop_type(reis); - - log_debug(portal, "passing Handle %d\n", handle); - return sd_bus_reply_method_return(m, "ua{sv}", 0, - 1, /* array size */ - "fd", "h", handle); -} - -static const sd_bus_vtable portal_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_METHOD("CreateSession", "a{sv}", "o", portal_create_session, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ConnectToEIS", "oa{sv}", "ua{sv}", portal_connect_to_eis, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_VTABLE_END, -}; - -static int -run(struct portal *portal) -{ - _unref_(sd_bus) *bus = NULL; - _unref_(sd_bus_slot) *slot = NULL; - int rc = sd_bus_open_user(&bus); - if (rc < 0) - return rc; - - rc = sd_bus_add_object_vtable(bus, &slot, - "/org/freedesktop/portal/desktop", - "org.freedesktop.portal.EmulatedInput", - portal_vtable, - portal); - if (rc < 0) - return rc; - - log_debug(portal, "Portal object at: /org/freedesktop/portal/desktop\n"); - - rc = sd_bus_request_name(bus, portal->busname, 0); - if (rc < 0) - return rc; - - log_debug(portal, "Portal DBus name: %s\n", portal->busname); - - _unref_(sd_event) *event = NULL; - rc = sd_event_default(&event); - if (rc < 0) - return rc; - - rc = sd_event_set_watchdog(event, true); - if (rc < 0) - return rc; - - rc = sd_bus_attach_event(bus, event, 0); - if (rc < 0) - return rc; - - return sd_event_loop(event); -} - -static void -usage(FILE *fp, const char *argv0) -{ - fprintf(fp, - "Usage: %s [--busname=a.b.c.d]\n" - "\n" - "Emulates an XDG Desktop portal for the org.freedesktop.portal.EmulatedInput interface\n" - "\n" - "Options:\n" - " --busname use the given busname instead of the default org.freedesktop.portal.Desktop\n" - "\n" - "This portal connects directly to the EIS instance at $LIBEI_SOCKET or, if unset, at \n" - "$XDG_RUNTIME_DIR/eis-0. It does **not** use the org.freedesktop.impl.portal.EmulatedInput\n" - "interface.\n" - "", - basename(argv0)); -} - -int -main(int argc, char **argv) -{ - _cleanup_free_ char *busname = xstrdup("org.freedesktop.portal.Desktop"); - - while (1) { - enum opts { - OPT_BUSNAME, - }; - static struct option long_opts[] = { - { "busname", required_argument, 0, OPT_BUSNAME}, - { "help", no_argument, 0, 'h'}, - { .name = NULL }, - }; - - int optind = 0; - int c = getopt_long(argc, argv, "h", long_opts, &optind); - if (c == -1) - break; - - switch(c) { - case 'h': - usage(stdout, argv[0]); - return EXIT_SUCCESS; - case OPT_BUSNAME: - free(busname); - busname = xstrdup(optarg); - break; - default: - usage(stderr, argv[0]); - return EXIT_FAILURE; - } - } - - list_init(&portal.sessions); - portal.busname = steal(&busname); - portal.logger = logger_new("portal", NULL); - logger_set_priority(portal.logger, LOGGER_DEBUG); - - int rc = run(&portal); - if (rc < 0) - fprintf(stderr, "Failed to start fake portal: %s\n", strerror(-rc)); - - logger_unref(portal.logger); - free(portal.busname); - return rc == 0; -}