[gnome-builder/gnome-builder-3-28] terminal: asynchronously determine user shell
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/gnome-builder-3-28] terminal: asynchronously determine user shell
- Date: Wed, 21 Mar 2018 21:40:08 +0000 (UTC)
commit 7d4d32ecc1cf465fed276a62bde6b9189fb6c3bb
Author: Christian Hergert <chergert redhat com>
Date: Wed Mar 21 14:33:35 2018 -0700
terminal: asynchronously determine user shell
This moves blocking portions of shell discovery out of the UI thread by
setting a sensible default and asynchronously querying the host system
during application startup.
Fixes #439
src/libide/application/ide-application.c | 3 +
src/libide/terminal/ide-terminal-private.h | 27 ++++++++
src/libide/terminal/ide-terminal-util.c | 101 +++++++++++++++++++++++++++++
src/libide/terminal/ide-terminal-util.h | 3 +-
src/plugins/terminal/gb-terminal-view.c | 73 +--------------------
5 files changed, 134 insertions(+), 73 deletions(-)
---
diff --git a/src/libide/application/ide-application.c b/src/libide/application/ide-application.c
index cf94bcf02..f24cb7a98 100644
--- a/src/libide/application/ide-application.c
+++ b/src/libide/application/ide-application.c
@@ -37,6 +37,7 @@
#include "application/ide-application-tests.h"
#include "application/ide-application-tool.h"
#include "modelines/modeline-parser.h"
+#include "terminal/ide-terminal-private.h"
#include "threading/ide-thread-pool.h"
#include "util/ide-battery-monitor.h"
#include "util/ide-flatpak.h"
@@ -530,6 +531,8 @@ ide_application_startup (GApplication *application)
if (self->mode == IDE_APPLICATION_MODE_PRIMARY)
ide_application_register_plugin_accessories (self);
+ _ide_guess_shell ();
+
ide_application_load_addins (self);
}
diff --git a/src/libide/terminal/ide-terminal-private.h b/src/libide/terminal/ide-terminal-private.h
new file mode 100644
index 000000000..4552e648f
--- /dev/null
+++ b/src/libide/terminal/ide-terminal-private.h
@@ -0,0 +1,27 @@
+/* ide-terminal-private.h
+ *
+ * Copyright 2018 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/>.
+ */
+
+#pragma once
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+void _ide_guess_shell (void);
+
+G_END_DECLS
diff --git a/src/libide/terminal/ide-terminal-util.c b/src/libide/terminal/ide-terminal-util.c
index 2446da967..9bb0e0ed1 100644
--- a/src/libide/terminal/ide-terminal-util.c
+++ b/src/libide/terminal/ide-terminal-util.c
@@ -23,10 +23,16 @@
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
+#include <vte/vte.h>
+#include "subprocess/ide-subprocess.h"
+#include "subprocess/ide-subprocess-launcher.h"
+#include "terminal/ide-terminal-private.h"
#include "terminal/ide-terminal-util.h"
#include "util/ptyintercept.h"
+static const gchar *user_shell = "/bin/sh";
+
gint
ide_vte_pty_create_slave (VtePty *pty)
{
@@ -40,3 +46,98 @@ ide_vte_pty_create_slave (VtePty *pty)
return pty_intercept_create_slave (master_fd, TRUE);
}
+
+/**
+ * ide_get_user_shell:
+ *
+ * Gets the user preferred shell on the host.
+ *
+ * If the background shell discovery has not yet finished due to
+ * slow or misconfigured getent on the host, this will provide a
+ * sensible fallback.
+ *
+ * Returns: (not nullable): a shell such as "/bin/sh"
+ */
+const gchar *
+ide_get_user_shell (void)
+{
+ return user_shell;
+}
+
+static void
+ide_guess_shell_communicate_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeSubprocess *subprocess = (IdeSubprocess *)object;
+ g_autoptr(GError) error = NULL;
+ g_autofree gchar *stdout_buf = NULL;
+
+ g_assert (IDE_IS_SUBPROCESS (subprocess));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (user_data == NULL);
+
+ if (!ide_subprocess_communicate_utf8_finish (subprocess, result, &stdout_buf, NULL, &error))
+ {
+ g_warning ("Failed to parse result from getent: %s", error->message);
+ return;
+ }
+
+ if (stdout_buf != NULL)
+ {
+ g_strstrip (stdout_buf);
+
+ if (stdout_buf[0] == '/')
+ user_shell = g_steal_pointer (&stdout_buf);
+ }
+}
+
+void
+_ide_guess_shell (void)
+{
+ g_autoptr(IdeSubprocessLauncher) launcher = NULL;
+ g_autoptr(IdeSubprocess) subprocess = NULL;
+ g_autofree gchar *command = NULL;
+ g_autoptr(GError) error = NULL;
+ g_auto(GStrv) argv = NULL;
+ const gchar *shell;
+
+ /*
+ * First ask VTE to guess, so we can use that while we discover
+ * the real shell asynchronously (and possibly outside the container).
+ */
+ if ((shell = vte_get_user_shell ()))
+ user_shell = g_strdup (shell);
+
+ command = g_strdup_printf ("sh -c 'getent passwd | grep ^%s: | head -n1 | cut -f 7 -d :'",
+ g_get_user_name ());
+
+ if (!g_shell_parse_argv (command, NULL, &argv, &error))
+ {
+ g_warning ("Failed to parse command into argv: %s", error->message);
+ return;
+ }
+
+ /*
+ * We don't use the runtime shell here, because we want to know
+ * what the host thinks the user shell should be.
+ */
+ launcher = ide_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE);
+
+ ide_subprocess_launcher_set_run_on_host (launcher, TRUE);
+ ide_subprocess_launcher_set_clear_env (launcher, FALSE);
+ ide_subprocess_launcher_set_cwd (launcher, g_get_home_dir ());
+ ide_subprocess_launcher_push_args (launcher, (const gchar * const *)argv);
+
+ if (!(subprocess = ide_subprocess_launcher_spawn (launcher, NULL, &error)))
+ {
+ g_warning ("Failed to spawn getent: %s", error->message);
+ return;
+ }
+
+ ide_subprocess_communicate_utf8_async (subprocess,
+ NULL,
+ NULL,
+ ide_guess_shell_communicate_cb,
+ NULL);
+}
diff --git a/src/libide/terminal/ide-terminal-util.h b/src/libide/terminal/ide-terminal-util.h
index 47fefdfb0..c94b40d12 100644
--- a/src/libide/terminal/ide-terminal-util.h
+++ b/src/libide/terminal/ide-terminal-util.h
@@ -24,6 +24,7 @@
G_BEGIN_DECLS
-int ide_vte_pty_create_slave (VtePty *pty);
+int ide_vte_pty_create_slave (VtePty *pty);
+const gchar *ide_get_user_shell (void);
G_END_DECLS
diff --git a/src/plugins/terminal/gb-terminal-view.c b/src/plugins/terminal/gb-terminal-view.c
index 84083a548..f239f6606 100644
--- a/src/plugins/terminal/gb-terminal-view.c
+++ b/src/plugins/terminal/gb-terminal-view.c
@@ -46,70 +46,12 @@ enum {
};
static GParamSpec *properties[LAST_PROP];
-static gchar *cached_shell;
static void gb_terminal_view_connect_terminal (GbTerminalView *self,
VteTerminal *terminal);
static void gb_terminal_respawn (GbTerminalView *self,
VteTerminal *terminal);
-static gchar *
-gb_terminal_view_discover_shell (GbTerminalView *self,
- GCancellable *cancellable,
- GError **error)
-{
- g_autoptr(IdeSubprocessLauncher) launcher = NULL;
- g_autoptr(IdeSubprocess) subprocess = NULL;
- g_autofree gchar *command = NULL;
- g_autofree gchar *stdout_buf = NULL;
- g_auto(GStrv) argv = NULL;
-
- g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
-
- if (cached_shell != NULL)
- return g_strdup (cached_shell);
-
- command = g_strdup_printf ("sh -c 'getent passwd | grep ^%s: | head -n1 | cut -f 7 -d :'",
- g_get_user_name ());
-
- if (!g_shell_parse_argv (command, NULL, &argv, error))
- return NULL;
-
- /*
- * We don't use the runtime shell here, because we want to know
- * what the host thinks the user shell should be.
- */
- launcher = ide_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE);
-
- ide_subprocess_launcher_set_run_on_host (launcher, TRUE);
- ide_subprocess_launcher_set_clear_env (launcher, FALSE);
- ide_subprocess_launcher_set_cwd (launcher, g_get_home_dir ());
- ide_subprocess_launcher_push_args (launcher, (const gchar * const *)argv);
-
- subprocess = ide_subprocess_launcher_spawn (launcher, cancellable, error);
-
- if (subprocess == NULL)
- return NULL;
-
- if (!ide_subprocess_communicate_utf8 (subprocess, NULL, cancellable, &stdout_buf, NULL, error))
- return NULL;
-
- if (stdout_buf != NULL)
- {
- g_strstrip (stdout_buf);
- if (stdout_buf[0] == '/')
- cached_shell = g_steal_pointer (&stdout_buf);
- }
-
- if (cached_shell == NULL)
- g_set_error_literal (error,
- G_IO_ERROR,
- G_IO_ERROR_FAILED,
- "Unknown error when discovering user shell");
-
- return g_strdup (cached_shell);
-}
-
static void
gb_terminal_view_wait_cb (GObject *object,
GAsyncResult *result,
@@ -206,20 +148,7 @@ gb_terminal_respawn (GbTerminalView *self,
build_manager = ide_context_get_build_manager (context);
pipeline = ide_build_manager_get_pipeline (build_manager);
- shell = gb_terminal_view_discover_shell (self, NULL, &error);
-
- if (shell == NULL)
- {
- g_warning ("Failed to discover user shell: %s", error->message);
-
- /* We prefer bash in flatpak over sh */
- if (ide_is_flatpak ())
- shell = g_strdup ("/bin/bash");
- else
- shell = vte_get_user_shell ();
-
- g_clear_error (&error);
- }
+ shell = g_strdup (ide_get_user_shell ());
pty = vte_terminal_pty_new_sync (terminal,
VTE_PTY_DEFAULT | VTE_PTY_NO_LASTLOG | VTE_PTY_NO_UTMP | VTE_PTY_NO_WTMP,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]