From b99e71ccf363324367160a2f4cb8eb92a1fd2928 Mon Sep 17 00:00:00 2001
From: Simon McVittie <smcv@collabora.com>
Date: Mon, 7 Sep 2020 15:57:05 +0100
Subject: [PATCH] wrap: Add ability to pass fds through -wrap and -adverb to
 the command

Part of pressure-vessel#6.

Signed-off-by: Simon McVittie <smcv@collabora.com>
---
 man/wrap.1.md |  6 ++++++
 src/wrap.c    | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 58 insertions(+)

diff --git a/man/wrap.1.md b/man/wrap.1.md
index 3c13a01da..eee4e4e82 100644
--- a/man/wrap.1.md
+++ b/man/wrap.1.md
@@ -78,6 +78,12 @@ pressure-vessel-wrap - run programs in a bubblewrap container
     With `--copy-runtime-into`, the prepared runtime will appear in
     a subdirectory of *DIR*.
 
+`--pass-fd` *FD*
+:   Pass the file descriptor *FD* (specified as a small positive integer)
+    from the parent process to the *COMMAND*. The default is to only pass
+    through file descriptors 0, 1 and 2
+    (**stdin**, **stdout** and **stderr**).
+
 `--runtime=`
 :   Use the current execution environment's /usr to provide /usr in
     the container.
diff --git a/src/wrap.c b/src/wrap.c
index ee3f7d23d..a5408662b 100644
--- a/src/wrap.c
+++ b/src/wrap.c
@@ -657,6 +657,7 @@ static gboolean opt_only_prepare = FALSE;
 static gboolean opt_remove_game_overlay = FALSE;
 static PvShell opt_shell = PV_SHELL_NONE;
 static GPtrArray *opt_ld_preload = NULL;
+static GArray *opt_pass_fds = NULL;
 static char *opt_runtime_base = NULL;
 static char *opt_runtime = NULL;
 static Tristate opt_share_home = TRISTATE_MAYBE;
@@ -686,6 +687,41 @@ opt_host_ld_preload_cb (const gchar *option_name,
   return TRUE;
 }
 
+static gboolean
+opt_pass_fd_cb (const char *name,
+                const char *value,
+                gpointer data,
+                GError **error)
+{
+  char *endptr;
+  gint64 i64 = g_ascii_strtoll (value, &endptr, 10);
+  int fd;
+  int fd_flags;
+
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+  g_return_val_if_fail (value != NULL, FALSE);
+
+  if (i64 < 0 || i64 > G_MAXINT || endptr == value || *endptr != '\0')
+    {
+      g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
+                   "Integer out of range or invalid: %s", value);
+      return FALSE;
+    }
+
+  fd = (int) i64;
+
+  fd_flags = fcntl (fd, F_GETFD);
+
+  if (fd_flags < 0)
+    return glnx_throw_errno_prefix (error, "Unable to receive --fd %d", fd);
+
+  if (opt_pass_fds == NULL)
+    opt_pass_fds = g_array_new (FALSE, FALSE, sizeof (int));
+
+  g_array_append_val (opt_pass_fds, fd);
+  return TRUE;
+}
+
 static gboolean
 opt_shell_cb (const gchar *option_name,
               const gchar *value,
@@ -925,6 +961,10 @@ static GOptionEntry options[] =
     "is run in a container. The empty string means use the graphics "
     "stack from container."
     "[Default: $PRESSURE_VESSEL_GRAPHICS_PROVIDER or '/run/host' or '/']", "PATH" },
+  { "pass-fd", '\0',
+    G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, opt_pass_fd_cb,
+    "Let the launched process inherit the given fd.",
+    NULL },
   { "remove-game-overlay", '\0',
     G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &opt_remove_game_overlay,
     "Disable the Steam Overlay. "
@@ -1946,6 +1986,17 @@ main (int argc,
                           "--subreaper",
                           NULL);
 
+  if (opt_pass_fds != NULL)
+    {
+      for (i = 0; i < opt_pass_fds->len; i++)
+        {
+          int fd = g_array_index (opt_pass_fds, int, i);
+
+          flatpak_bwrap_add_fd (bwrap, fd);
+          flatpak_bwrap_add_arg_printf (bwrap, "--pass-fd=%d", fd);
+        }
+    }
+
   switch (opt_shell)
     {
       case PV_SHELL_AFTER:
@@ -2087,6 +2138,7 @@ out:
   g_clear_pointer (&opt_fake_home, g_free);
   g_clear_pointer (&opt_runtime_base, g_free);
   g_clear_pointer (&opt_runtime, g_free);
+  g_clear_pointer (&opt_pass_fds, g_array_unref);
 
   g_debug ("Exiting with status %d", ret);
   return ret;
-- 
GitLab