[gnome-builder] plugins/deviced: port to GTK 4



commit 7586f8013708b3263ea8d89052314260f71bca7e
Author: Christian Hergert <chergert redhat com>
Date:   Mon Jul 11 22:32:16 2022 -0700

    plugins/deviced: port to GTK 4
    
     - Remove IdeRunner usage
     - Add a gnome-builder-deviced helper program that can be executed as
       part of the IdeRunContext command line chain.

 src/plugins/deviced/deviced.plugin                |   3 +-
 src/plugins/deviced/gbp-deviced-deploy-strategy.c |  65 +---
 src/plugins/deviced/gbp-deviced-runner.c          | 435 ----------------------
 src/plugins/deviced/gbp-deviced-runner.h          |  39 --
 src/plugins/deviced/gnome-builder-deviced.c       | 393 +++++++++++++++++++
 src/plugins/deviced/meson.build                   |  16 +-
 6 files changed, 415 insertions(+), 536 deletions(-)
---
diff --git a/src/plugins/deviced/deviced.plugin b/src/plugins/deviced/deviced.plugin
index 25811b59d..e6cca668c 100644
--- a/src/plugins/deviced/deviced.plugin
+++ b/src/plugins/deviced/deviced.plugin
@@ -2,8 +2,9 @@
 Authors=Christian Hergert <christian hergert me>
 Builtin=true
 Copyright=Copyright © 2017 Christian Hergert
-Depends=editor;debuggerui;terminal;deviceui;flatpak;
+Depends=flatpak;
 Description=Integration with deviced devices
 Embedded=_gbp_deviced_register_types
 Module=deviced
 Name=Deviced
+X-Category=devices
diff --git a/src/plugins/deviced/gbp-deviced-deploy-strategy.c 
b/src/plugins/deviced/gbp-deviced-deploy-strategy.c
index f8877cf5a..3d1f6d636 100644
--- a/src/plugins/deviced/gbp-deviced-deploy-strategy.c
+++ b/src/plugins/deviced/gbp-deviced-deploy-strategy.c
@@ -25,7 +25,6 @@
 
 #include "gbp-deviced-deploy-strategy.h"
 #include "gbp-deviced-device.h"
-#include "gbp-deviced-runner.h"
 
 struct _GbpDevicedDeployStrategy
 {
@@ -99,12 +98,16 @@ gbp_deviced_deploy_strategy_load_async (IdeDeployStrategy   *strategy,
 }
 
 static gboolean
-gbp_deviced_deploy_strategy_load_finish (IdeDeployStrategy *self,
-                                         GAsyncResult *result,
-                                         GError **error)
+gbp_deviced_deploy_strategy_load_finish (IdeDeployStrategy  *self,
+                                         GAsyncResult       *result,
+                                         int                *priority,
+                                         GError            **error)
 {
-  g_return_val_if_fail (GBP_IS_DEVICED_DEPLOY_STRATEGY (self), FALSE);
-  g_return_val_if_fail (ide_task_is_valid (result, self), FALSE);
+  g_assert (GBP_IS_DEVICED_DEPLOY_STRATEGY (self));
+  g_assert (ide_task_is_valid (result, self));
+  g_assert (priority != NULL);
+
+  *priority = -100;
 
   return ide_task_propagate_boolean (IDE_TASK (result), error);
 }
@@ -372,54 +375,6 @@ gbp_deviced_deploy_strategy_deploy_finish (IdeDeployStrategy *self,
   return ide_task_propagate_boolean (IDE_TASK (result), error);
 }
 
-static void
-gbp_deviced_deploy_strategy_create_runner_async (IdeDeployStrategy   *strategy,
-                                                 IdePipeline         *pipeline,
-                                                 GCancellable        *cancellable,
-                                                 GAsyncReadyCallback  callback,
-                                                 gpointer             user_data)
-{
-  GbpDevicedDeployStrategy *self = (GbpDevicedDeployStrategy *)strategy;
-  g_autoptr(IdeTask) task = NULL;
-  IdeDevice *device = NULL;
-  IdeConfig *config = NULL;
-  GbpDevicedRunner *runner;
-
-  IDE_ENTRY;
-
-  g_assert (GBP_IS_DEVICED_DEPLOY_STRATEGY (self));
-  g_assert (IDE_IS_PIPELINE (pipeline));
-  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
-
-  task = ide_task_new (self, cancellable, callback, user_data);
-  ide_task_set_source_tag (task, gbp_deviced_deploy_strategy_create_runner_async);
-
-  device = ide_pipeline_get_device (pipeline);
-  config = ide_pipeline_get_config (pipeline);
-
-  g_assert (GBP_IS_FLATPAK_MANIFEST (config));
-  g_assert (GBP_IS_DEVICED_DEVICE (device));
-
-  g_assert (IDE_IS_CONTEXT (ide_object_get_context (IDE_OBJECT (self))));
-
-  runner = gbp_deviced_runner_new (GBP_DEVICED_DEVICE (device));
-  ide_object_append (IDE_OBJECT (pipeline), IDE_OBJECT (runner));
-  ide_task_return_object (task, runner);
-
-  IDE_EXIT;
-}
-
-static IdeRunner *
-gbp_deviced_deploy_strategy_create_runner_finish (IdeDeployStrategy  *self,
-                                                  GAsyncResult       *result,
-                                                  GError            **error)
-{
-  g_return_val_if_fail (GBP_IS_DEVICED_DEPLOY_STRATEGY (self), FALSE);
-  g_return_val_if_fail (ide_task_is_valid (result, self), FALSE);
-
-  return ide_task_propagate_object (IDE_TASK (result), error);
-}
-
 static void
 gbp_deviced_deploy_strategy_class_init (GbpDevicedDeployStrategyClass *klass)
 {
@@ -429,8 +384,6 @@ gbp_deviced_deploy_strategy_class_init (GbpDevicedDeployStrategyClass *klass)
   strategy_class->load_finish = gbp_deviced_deploy_strategy_load_finish;
   strategy_class->deploy_async = gbp_deviced_deploy_strategy_deploy_async;
   strategy_class->deploy_finish = gbp_deviced_deploy_strategy_deploy_finish;
-  strategy_class->create_runner_async = gbp_deviced_deploy_strategy_create_runner_async;
-  strategy_class->create_runner_finish = gbp_deviced_deploy_strategy_create_runner_finish;
 }
 
 static void
diff --git a/src/plugins/deviced/gnome-builder-deviced.c b/src/plugins/deviced/gnome-builder-deviced.c
new file mode 100644
index 000000000..bc2802c62
--- /dev/null
+++ b/src/plugins/deviced/gnome-builder-deviced.c
@@ -0,0 +1,393 @@
+/* gnome-builder-deviced.c
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <libdeviced.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+
+static GMainLoop *main_loop;
+static GInetSocketAddress *address;
+static DevdProcessService *procsvc;
+static char *pty_id;
+static char *process_id;
+static struct {
+  gboolean exited;
+  int exit_code;
+  int term_sig;
+} exit_info;
+static guint fail_source;
+static guint signal_source;
+static int signal_to_proxy;
+static char *opt_address;
+static char *opt_app_id;
+static int opt_port;
+static int opt_pty_fd = -1;
+static int opt_timeout_seconds = 10;
+static GOptionEntry options[] = {
+  { "address", 0, 0, G_OPTION_ARG_STRING, &opt_address, N_("The device address") },
+  { "port", 0, 0, G_OPTION_ARG_INT, &opt_port, N_("The device port number") },
+  { "app-id", 0, 0, G_OPTION_ARG_STRING, &opt_app_id, N_("The application to run") },
+  { "pty-fd", 0, 0, G_OPTION_ARG_INT, &opt_pty_fd, N_("A PTY to bidirectionally proxy to the device") },
+  { "timeout", 0, 0, G_OPTION_ARG_INT, &opt_timeout_seconds, N_("Number of seconds to wait for the deviced 
peer to appear") },
+  { NULL }
+};
+
+static void
+proxy_signal (int signum)
+{
+  /* We need to be signal handler safe here of course, which means no
+   * allocations, no locks, etc. Basically all we can do is read/write to FDs
+   * or set some variables. So we just set the signal to be proxied and handle
+   * it from the main loop on the next cycle through.
+   */
+  signal_to_proxy = signum;
+}
+
+static struct {
+  int signum;
+  sighandler_t previous;
+} proxied_signals[] = {
+  { SIGHUP },
+  { SIGINT },
+  { SIGQUIT },
+  { SIGUSR1 },
+  { SIGUSR2 },
+  { SIGUSR2 },
+  { SIGTERM },
+#if 0
+/* These signals cannot be handled and therefore cannot be proxied.
+ * To do this, we'd need to create a monitor process that watches
+ * us and sends the signal to the peer. Probably more effort than
+ * it is worth if we're going to drop this and move towards Bonsai
+ * anyway in the future.
+ */
+  { SIGSTOP },
+  { SIGKILL },
+#endif
+};
+
+static void
+setup_signal_handling (void)
+{
+  /* Note: We could use signalfd() here on Linux and do this much
+   * better than spinning our main loop occasionally. But that would
+   * still require porting to other platforms and quite frankly it's
+   * not really worth the effort due to how short the lifespan is of
+   * applications running.
+   */
+  for (guint i = 0; i < G_N_ELEMENTS (proxied_signals); i++)
+    proxied_signals[i].previous = signal (proxied_signals[i].signum, proxy_signal);
+}
+
+static void
+tear_down_signal_handling (void)
+{
+  for (guint i = 0; i < G_N_ELEMENTS (proxied_signals); i++)
+    signal (proxied_signals[i].signum, proxied_signals[i].previous);
+}
+
+static gboolean
+fail_to_connect_cb (gpointer data)
+{
+  g_error ("Failed to locate target device, exiting!");
+  return G_SOURCE_REMOVE;
+}
+
+static void
+destroy_pty_cb (GObject      *object,
+                GAsyncResult *result,
+                gpointer      user_data)
+{
+  DevdProcessService *process = (DevdProcessService *)object;
+  g_autoptr(GError) error = NULL;
+
+  g_assert (DEVD_IS_PROCESS_SERVICE (process));
+
+  if (!devd_process_service_destroy_pty_finish (process, result, &error))
+    g_error ("Failed to destroy PTY: %s", error->message);
+
+  g_clear_pointer (&process_id, g_free);
+
+  tear_down_signal_handling ();
+
+  g_main_loop_quit (main_loop);
+
+  if (exit_info.exited)
+    exit (exit_info.exit_code);
+  else
+    kill (getpid (), exit_info.term_sig);
+}
+
+static void
+wait_for_process_cb (GObject      *object,
+                     GAsyncResult *result,
+                     gpointer      user_data)
+{
+  DevdProcessService *process = (DevdProcessService *)object;
+  g_autoptr(GError) error = NULL;
+
+  g_assert (DEVD_IS_PROCESS_SERVICE (process));
+  g_assert (G_IS_ASYNC_RESULT (result));
+
+  if (!devd_process_service_wait_for_process_finish (process,
+                                                     result,
+                                                     &exit_info.exited,
+                                                     &exit_info.exit_code,
+                                                     &exit_info.term_sig,
+                                                     &error))
+    g_error ("Failed to wait for process exit: %s", error->message);
+
+  g_printerr ("Process exited\n");
+
+  /* Clean up our PTY if we can */
+  devd_process_service_destroy_pty_async (process,
+                                          pty_id,
+                                          NULL,
+                                          destroy_pty_cb,
+                                          NULL);
+}
+
+static void
+client_run_app_cb (GObject      *object,
+                   GAsyncResult *result,
+                   gpointer      user_data)
+{
+  DevdClient *client = (DevdClient *)object;
+  g_autoptr(GError) error = NULL;
+
+  g_assert (DEVD_IS_CLIENT (client));
+  g_assert (G_IS_ASYNC_RESULT (result));
+
+  if (!(process_id = devd_client_run_app_finish (client, result, &error)))
+    g_error ("Failed to launch process: %s", error->message);
+
+  setup_signal_handling ();
+
+  devd_process_service_wait_for_process_async (procsvc,
+                                               process_id,
+                                               NULL,
+                                               wait_for_process_cb,
+                                               NULL);
+}
+
+static void
+process_create_pty_cb (GObject      *object,
+                       GAsyncResult *result,
+                       gpointer      user_data)
+{
+  DevdProcessService *process = (DevdProcessService *)object;
+  g_autoptr(GError) error = NULL;
+  DevdClient *client;
+
+  g_assert (DEVD_IS_PROCESS_SERVICE (process));
+  g_assert (G_IS_ASYNC_RESULT (result));
+
+  if (!(pty_id = devd_process_service_create_pty_finish (process, result, &error)))
+    g_error ("Failed to create PTY: %s", error->message);
+
+  procsvc = g_object_ref (process);
+  client = devd_service_get_client (DEVD_SERVICE (process));
+
+  devd_client_run_app_async (client,
+                             "flatpak",
+                             opt_app_id,
+                             pty_id,
+                             NULL,
+                             client_run_app_cb,
+                             NULL);
+}
+
+static void
+client_connect_cb (GObject      *object,
+                   GAsyncResult *result,
+                   gpointer      user_data)
+{
+  DevdClient *client = (DevdClient *)object;
+  g_autoptr(DevdProcessService) process = NULL;
+  g_autoptr(GError) error = NULL;
+
+  g_assert (DEVD_IS_CLIENT (client));
+  g_assert (G_IS_ASYNC_RESULT (result));
+
+  if (!devd_client_connect_finish (client, result, &error))
+    g_error ("Failed to connect to device: %s", error->message);
+
+  if (!(process = devd_process_service_new (client, &error)))
+    g_error ("Failed to locate process service: %s", error->message);
+
+  g_clear_handle_id (&fail_source, g_source_remove);
+
+  devd_process_service_create_pty_async (process,
+                                         opt_pty_fd,
+                                         NULL,
+                                         process_create_pty_cb,
+                                         NULL);
+}
+
+static gboolean
+inet_socket_address_equal (GSocketAddress *a,
+                           GSocketAddress *b)
+{
+  gsize a_size;
+  gsize b_size;
+  gpointer a_data;
+  gpointer b_data;
+
+  g_assert (G_IS_SOCKET_ADDRESS (a));
+  g_assert (G_IS_SOCKET_ADDRESS (b));
+
+  a_size = g_socket_address_get_native_size (a);
+  b_size = g_socket_address_get_native_size (b);
+
+  if (a_size != b_size)
+    return FALSE;
+
+  a_data = g_alloca0 (a_size);
+  b_data = g_alloca0 (b_size);
+
+  if (!g_socket_address_to_native (a, a_data, a_size, NULL) ||
+      !g_socket_address_to_native (b, b_data, b_size, NULL))
+    return FALSE;
+
+  return memcmp (a_data, b_data, a_size) == 0;
+}
+
+static void
+device_added_cb (DevdBrowser *browser,
+                 DevdDevice  *device,
+                 gpointer     user_data)
+{
+  g_assert (DEVD_IS_BROWSER (browser));
+  g_assert (DEVD_IS_DEVICE (device));
+
+  if (DEVD_IS_NETWORK_DEVICE (device))
+    {
+      GInetSocketAddress *device_address = devd_network_device_get_address (DEVD_NETWORK_DEVICE (device));
+
+      if (inet_socket_address_equal (G_SOCKET_ADDRESS (address),
+                                     G_SOCKET_ADDRESS (device_address)))
+        {
+          g_autoptr(DevdClient) client = devd_device_create_client (device);
+
+          g_signal_handlers_disconnect_by_func (browser,
+                                                G_CALLBACK (device_added_cb),
+                                                user_data);
+
+          devd_client_connect_async (client,
+                                     NULL,
+                                     client_connect_cb,
+                                     NULL);
+        }
+    }
+}
+
+static void
+device_removed_cb (DevdBrowser *browser,
+                   DevdDevice  *device,
+                   gpointer     user_data)
+{
+  g_assert (DEVD_IS_BROWSER (browser));
+  g_assert (DEVD_IS_DEVICE (device));
+
+  if (DEVD_IS_NETWORK_DEVICE (device))
+    {
+      GInetSocketAddress *device_address = devd_network_device_get_address (DEVD_NETWORK_DEVICE (device));
+
+      if (inet_socket_address_equal (G_SOCKET_ADDRESS (address),
+                                     G_SOCKET_ADDRESS (device_address)))
+        {
+          /* We might not have, but avahi says so and we just need to be
+           * extra careful so we don't hang indefinitely.
+           */
+          g_printerr ("lost connection from device\n");
+          exit (EXIT_FAILURE);
+        }
+    }
+}
+
+static void
+load_cb (GObject      *object,
+         GAsyncResult *result,
+         gpointer      user_data)
+{
+  DevdBrowser *browser = (DevdBrowser *)object;
+  g_autoptr(GError) error = NULL;
+
+  g_assert (DEVD_IS_BROWSER (browser));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (user_data == NULL);
+
+  if (!devd_browser_load_finish (browser, result, &error))
+    g_error ("%s", error->message);
+}
+
+static gboolean
+signal_source_cb (gpointer data)
+{
+  if (signal_to_proxy != 0 && procsvc != NULL && process_id != NULL)
+    {
+      devd_process_service_send_signal (procsvc, process_id, signal_to_proxy);
+      signal_to_proxy = 0;
+    }
+
+  return G_SOURCE_CONTINUE;
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  g_autoptr(GOptionContext) context = NULL;
+  g_autoptr(DevdBrowser) browser = NULL;
+  g_autoptr(GError) error = NULL;
+
+  context = g_option_context_new ("gnome-builder-deviced");
+  g_option_context_add_main_entries (context, options, GETTEXT_PACKAGE);
+
+  if (!g_option_context_parse (context, &argc, &argv, &error))
+    {
+      g_printerr ("%s\n", error->message);
+      return EXIT_FAILURE;
+    }
+
+  if (opt_address == NULL || opt_app_id == NULL)
+    return EXIT_FAILURE;
+
+  if (!(address = G_INET_SOCKET_ADDRESS (g_inet_socket_address_new_from_string (opt_address, opt_port))))
+    return EXIT_FAILURE;
+
+  main_loop = g_main_loop_new (NULL, FALSE);
+
+  browser = devd_browser_new ();
+  g_signal_connect (browser, "device-added", G_CALLBACK (device_added_cb), NULL);
+  g_signal_connect (browser, "device-removed", G_CALLBACK (device_removed_cb), NULL);
+  devd_browser_load_async (browser, NULL, load_cb, NULL);
+
+  fail_source = g_timeout_add_seconds (opt_timeout_seconds, fail_to_connect_cb, NULL);
+  signal_source = g_timeout_add (500, signal_source_cb, NULL);
+
+  g_main_loop_run (main_loop);
+
+  return EXIT_SUCCESS;
+}
diff --git a/src/plugins/deviced/meson.build b/src/plugins/deviced/meson.build
index 8eb16b9e3..adf533266 100644
--- a/src/plugins/deviced/meson.build
+++ b/src/plugins/deviced/meson.build
@@ -4,12 +4,13 @@ if not get_option('plugin_flatpak')
   error('-Dplugin_flatpak=true is required to enable deviced')
 endif
 
+libdeviced_dep = dependency('libdeviced', version: '>=3.27.4')
+
 plugins_sources += files([
   'deviced-plugin.c',
   'gbp-deviced-deploy-strategy.c',
   'gbp-deviced-device.c',
   'gbp-deviced-device-provider.c',
-  'gbp-deviced-runner.c',
 ])
 
 plugin_deviced_resources = gnome.compile_resources(
@@ -18,10 +19,15 @@ plugin_deviced_resources = gnome.compile_resources(
   c_name: 'gbp_deviced',
 )
 
-plugins_deps += [
-  dependency('libdeviced', version: '>=3.27.4'),
-]
-
+plugins_deps += [libdeviced_dep]
 plugins_sources += plugin_deviced_resources
 
+gnome_builder_deviced_sources = ['gnome-builder-deviced.c']
+gnome_builder_deviced_deps = [libdeviced_dep]
+gnome_builder_deviced = executable('gnome-builder-deviced', gnome_builder_deviced_sources,
+           install: true,
+       install_dir: get_option('libexecdir'),
+      dependencies: gnome_builder_deviced_deps,
+)
+
 endif


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]