[gnome-builder] terminal: manage spawning pty child manually
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] terminal: manage spawning pty child manually
- Date: Fri, 16 Sep 2016 04:00:51 +0000 (UTC)
commit 0d2ebaadd2b194a6f956271ec177751af6bd44a9
Author: Christian Hergert <chergert redhat com>
Date: Thu Sep 15 21:00:09 2016 -0700
terminal: manage spawning pty child manually
So that we can use an alternate method of creating a TTY (such as via
the HostCommand flatpak interface), we need to take control of the spawn
process in the terminal.
We start by getting the pty master from VtePty, and then using that to
create our pts child. That FD may be passed along to flatpaks HostCommand
so that we can get a host shell with a child psuedo terminal.
plugins/terminal/gb-terminal-view.c | 160 ++++++++++++++++++++++++++--------
1 files changed, 122 insertions(+), 38 deletions(-)
---
diff --git a/plugins/terminal/gb-terminal-view.c b/plugins/terminal/gb-terminal-view.c
index 4c8d7e9..cfce000 100644
--- a/plugins/terminal/gb-terminal-view.c
+++ b/plugins/terminal/gb-terminal-view.c
@@ -16,9 +16,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <fcntl.h>
#include <glib/gi18n.h>
#include <ide.h>
+#include <stdlib.h>
#include <vte/vte.h>
+#include <unistd.h>
#include "gb-terminal.h"
#include "gb-terminal-view.h"
@@ -61,21 +64,84 @@ static const GdkRGBA solarized_palette[] =
};
static void gb_terminal_view_connect_terminal (GbTerminalView *self,
- VteTerminal *terminal);
+ VteTerminal *terminal);
+static void gb_terminal_respawn (GbTerminalView *self,
+ VteTerminal *terminal);
+
+static void
+fd_set_cloexec (int fd)
+{
+ int flags;
+
+ if (fd == -1)
+ return;
+
+ flags = fcntl (fd, F_GETFD, 0);
+
+ if (flags < 0)
+ return;
+
+ fcntl (fd, F_SETFD, flags | FD_CLOEXEC);
+}
+
+static void
+gb_terminal_view_wait_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeSubprocess *subprocess = (IdeSubprocess *)object;
+ VteTerminal *terminal = user_data;
+ GbTerminalView *self;
+ g_autoptr(GError) error = NULL;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_SUBPROCESS (subprocess));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (VTE_IS_TERMINAL (terminal));
+
+ if (!ide_subprocess_wait_finish (subprocess, result, &error))
+ {
+ g_warning ("%s", error->message);
+ IDE_GOTO (failure);
+ }
+
+ self = (GbTerminalView *)gtk_widget_get_ancestor (GTK_WIDGET (terminal), GB_TYPE_TERMINAL_VIEW);
+ if (self == NULL)
+ IDE_GOTO (failure);
+
+ if (!ide_widget_action (GTK_WIDGET (self), "view-stack", "close", NULL))
+ {
+ if (!gtk_widget_in_destruction (GTK_WIDGET (terminal)))
+ gb_terminal_respawn (self, terminal);
+ }
+
+failure:
+ g_clear_object (&terminal);
+
+ IDE_EXIT;
+}
static void
gb_terminal_respawn (GbTerminalView *self,
VteTerminal *terminal)
{
g_autoptr(GPtrArray) args = NULL;
+ g_autoptr(IdeSubprocess) subprocess = NULL;
+ g_autoptr(IdeSubprocessLauncher) launcher = NULL;
g_autofree gchar *workpath = NULL;
GtkWidget *toplevel;
GError *error = NULL;
IdeContext *context;
IdeVcs *vcs;
+ VtePty *pty = NULL;
GFile *workdir;
- GPid child_pid;
gint64 now;
+ int master_fd = -1;
+ int tty_fd = -1;
+ char name[PATH_MAX + 1];
+
+ IDE_ENTRY;
g_assert (GB_IS_TERMINAL_VIEW (self));
@@ -83,12 +149,12 @@ gb_terminal_respawn (GbTerminalView *self,
toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self));
if (!IDE_IS_WORKBENCH (toplevel))
- return;
+ IDE_EXIT;
/* Prevent flapping */
now = g_get_monotonic_time ();
if ((now - self->last_respawn) < (G_USEC_PER_SEC / 10))
- return;
+ IDE_EXIT;
self->last_respawn = now;
context = ide_workbench_get_context (IDE_WORKBENCH (toplevel));
@@ -100,41 +166,65 @@ gb_terminal_respawn (GbTerminalView *self,
g_ptr_array_add (args, vte_get_user_shell ());
g_ptr_array_add (args, NULL);
- vte_terminal_spawn_sync (terminal,
- VTE_PTY_DEFAULT | VTE_PTY_NO_LASTLOG | VTE_PTY_NO_UTMP | VTE_PTY_NO_WTMP,
- workpath,
- (gchar **)args->pdata,
- NULL,
- G_SPAWN_DEFAULT,
- NULL,
- NULL,
- &child_pid,
- NULL,
- &error);
+ pty = vte_terminal_pty_new_sync (terminal,
+ VTE_PTY_DEFAULT | VTE_PTY_NO_LASTLOG | VTE_PTY_NO_UTMP | VTE_PTY_NO_WTMP,
+ NULL,
+ &error);
+ if (pty == NULL)
+ IDE_GOTO (failure);
+
+ vte_terminal_set_pty (terminal, pty);
+
+ if (-1 == (master_fd = vte_pty_get_fd (pty)))
+ IDE_GOTO (failure);
+
+ if (grantpt (master_fd) != 0)
+ IDE_GOTO (failure);
+
+ if (unlockpt (master_fd) != 0)
+ IDE_GOTO (failure);
+
+ if (ptsname_r (master_fd, name, sizeof name - 1) != 0)
+ IDE_GOTO (failure);
+
+ if (-1 == (tty_fd = open (name, O_RDWR)))
+ IDE_GOTO (failure);
+
+ fd_set_cloexec (tty_fd);
+
+ /* XXX: It would be nice to allow using the runtimes launcher */
+ launcher = ide_subprocess_launcher_new (0);
+ ide_subprocess_launcher_set_run_on_host (launcher, TRUE);
+ ide_subprocess_launcher_set_clear_env (launcher, FALSE);
+ ide_subprocess_launcher_set_cwd (launcher, workpath);
+ ide_subprocess_launcher_push_args (launcher, (const gchar * const *)args->pdata);
+ ide_subprocess_launcher_take_stdin_fd (launcher, dup (tty_fd));
+ ide_subprocess_launcher_take_stdout_fd (launcher, dup (tty_fd));
+ ide_subprocess_launcher_take_stderr_fd (launcher, dup (tty_fd));
+ ide_subprocess_launcher_setenv (launcher, "TERM", "xterm-256color", TRUE);
+
+ subprocess = ide_subprocess_launcher_spawn_sync (launcher, NULL, &error);
+ if (subprocess == NULL)
+ IDE_GOTO (failure);
+
+ ide_subprocess_wait_async (subprocess,
+ NULL,
+ gb_terminal_view_wait_cb,
+ g_object_ref (terminal));
+
+failure:
+ if (tty_fd != -1)
+ close (tty_fd);
+
+ g_clear_object (&pty);
if (error != NULL)
{
g_warning ("%s", error->message);
g_clear_error (&error);
- return;
}
- vte_terminal_watch_child (terminal, child_pid);
-}
-
-static void
-child_exited_cb (VteTerminal *terminal,
- gint exit_status,
- GbTerminalView *self)
-{
- g_assert (VTE_IS_TERMINAL (terminal));
- g_assert (GB_IS_TERMINAL_VIEW (self));
-
- if (!ide_widget_action (GTK_WIDGET (self), "view-stack", "close", NULL))
- {
- if (!gtk_widget_in_destruction (GTK_WIDGET (terminal)))
- gb_terminal_respawn (self, terminal);
- }
+ IDE_EXIT;
}
static void
@@ -481,12 +571,6 @@ gb_terminal_view_connect_terminal (GbTerminalView *self,
0);
g_signal_connect_object (terminal,
- "child-exited",
- G_CALLBACK (child_exited_cb),
- self,
- 0);
-
- g_signal_connect_object (terminal,
"focus-in-event",
G_CALLBACK (focus_in_event_cb),
self,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]