[gnome-builder/gnome-builder-43] libide/io: use preferred shell for host $PATH discovery



commit 7d2153066d4c856cbb929d990ece7b9fe7e08151
Author: Christian Hergert <chergert redhat com>
Date:   Tue Sep 27 11:42:49 2022 -0700

    libide/io: use preferred shell for host $PATH discovery
    
    Previously, we tried to discover the user's preferred shell and host $PATH
    environment variables concurrently. That prevents us from being able to use
    the users preferred shell to discover $PATH.
    
    The side-effect of this is that we may not pick up environment changes that
    are defined in places like ~/.bashrc when bash is the preferred shell.
    
    This fixes that by asynchronously discovering $SHELL, then using that to
    run $SHELL -l -c 'echo $PATH' assuming we found a shell that we know
    supports -l (for --login shell) and -c (for command). Otherwise, we still
    fallback to using /bin/sh for discovery.
    
    This fixes an issue where the podman plugin failed to discover tooling like
    rust-analyzer being available within the container because we didn't pick
    up environment settings from .bashrc which might give access to rustc,
    cargo, rust-analyzer, etc.
    
    Fixes #1822

 src/libide/io/ide-shell-private.h |   5 +-
 src/libide/io/ide-shell.c         | 178 +++++++++++++++++++++++++++++---------
 src/main.c                        |   5 +-
 3 files changed, 142 insertions(+), 46 deletions(-)
---
diff --git a/src/libide/io/ide-shell-private.h b/src/libide/io/ide-shell-private.h
index a10695804..d9f8e3135 100644
--- a/src/libide/io/ide-shell-private.h
+++ b/src/libide/io/ide-shell-private.h
@@ -1,6 +1,6 @@
 /* ide-shell-private.h
  *
- * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ * Copyright 2018-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
@@ -24,7 +24,6 @@
 
 G_BEGIN_DECLS
 
-void _ide_guess_shell     (void);
-void _ide_guess_user_path (void);
+void _ide_shell_init (void);
 
 G_END_DECLS
diff --git a/src/libide/io/ide-shell.c b/src/libide/io/ide-shell.c
index 77e141206..77b9224fb 100644
--- a/src/libide/io/ide-shell.c
+++ b/src/libide/io/ide-shell.c
@@ -1,6 +1,6 @@
 /* ide-shell.c
  *
- * Copyright 2021 Christian Hergert <chergert redhat com>
+ * Copyright 2021-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
@@ -79,18 +79,24 @@ ide_guess_shell_communicate_cb (GObject      *object,
                                 gpointer      user_data)
 {
   IdeSubprocess *subprocess = (IdeSubprocess *)object;
+  g_autoptr(IdeTask) task = user_data;
   g_autoptr(GError) error = NULL;
   g_autofree gchar *stdout_buf = NULL;
-  const char *key = user_data;
+  const char *key;
 
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
   g_assert (IDE_IS_SUBPROCESS (subprocess));
   g_assert (G_IS_ASYNC_RESULT (result));
-  g_assert (key != NULL);
+  g_assert (IDE_IS_TASK (task));
+
+  key = ide_task_get_task_data (task);
 
   if (!ide_subprocess_communicate_utf8_finish (subprocess, result, &stdout_buf, NULL, &error))
     {
-      g_warning ("Failure to parse host information: %s", error->message);
-      return;
+      ide_task_return_error (task, g_steal_pointer (&error));
+      IDE_EXIT;
     }
 
   if (stdout_buf != NULL)
@@ -108,17 +114,36 @@ ide_guess_shell_communicate_cb (GObject      *object,
       if (!ide_str_empty0 (stdout_buf))
         user_default_path = g_steal_pointer (&stdout_buf);
     }
+  else
+    {
+      g_critical ("Unknown key %s", key);
+    }
+
+  ide_task_return_boolean (task, TRUE);
+
+  IDE_EXIT;
 }
 
-void
-_ide_guess_shell (void)
+static void
+_ide_guess_shell (GCancellable        *cancellable,
+                  GAsyncReadyCallback  callback,
+                  gpointer             user_data)
 {
+  g_autoptr(IdeTask) task = NULL;
   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;
 
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = ide_task_new (NULL, cancellable, callback, user_data);
+  ide_task_set_task_data (task, g_strdup ("SHELL"), g_free);
+
 #ifdef __APPLE__
   command = g_strdup_printf ("sh -c 'dscacheutil -q user -a name %s | grep ^shell: | cut -f 2 -d \" \"'",
                              g_get_user_name ());
@@ -129,9 +154,8 @@ _ide_guess_shell (void)
 
   if (!g_shell_parse_argv (command, NULL, &argv, &error))
     {
-      g_warning ("Failed to parse command into argv: %s",
-                 error ? error->message : "unknown error");
-      return;
+      ide_task_return_error (task, g_steal_pointer (&error));
+      IDE_EXIT;
     }
 
   /*
@@ -139,60 +163,73 @@ _ide_guess_shell (void)
    * 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_task_return_error (task, g_steal_pointer (&error));
+  else
+    ide_subprocess_communicate_utf8_async (subprocess,
+                                           NULL,
+                                           cancellable,
+                                           ide_guess_shell_communicate_cb,
+                                           g_steal_pointer (&task));
 
-  ide_subprocess_communicate_utf8_async (subprocess,
-                                         NULL,
-                                         NULL,
-                                         ide_guess_shell_communicate_cb,
-                                         (gpointer)"SHELL");
+  IDE_EXIT;
 }
 
-void
-_ide_guess_user_path (void)
+static void
+_ide_guess_user_path (GCancellable        *cancellable,
+                      GAsyncReadyCallback  callback,
+                      gpointer             user_data)
 {
   g_autoptr(IdeSubprocessLauncher) launcher = NULL;
   g_autoptr(IdeSubprocess) subprocess = NULL;
-  g_autofree gchar *command = NULL;
+  g_autoptr(IdeTask) task = NULL;
   g_autoptr(GError) error = NULL;
-  g_auto(GStrv) argv = NULL;
 
-  command = g_strdup_printf ("sh --login -c 'echo $PATH'");
+  IDE_ENTRY;
 
-  if (!g_shell_parse_argv (command, NULL, &argv, &error))
-    {
-      g_warning ("Failed to parse command into argv: %s",
-                 error ? error->message : "unknown error");
-      return;
-    }
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
 
+  task = ide_task_new (NULL, cancellable, callback, user_data);
+  ide_task_set_task_data (task, g_strdup ("PATH"), g_free);
+
+  /* This works by running 'echo $PATH' on the host, preferably
+   * through the user $SHELL we discovered.
+   */
   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)))
+  if (ide_shell_supports_dash_c (user_shell))
     {
-      g_warning ("Failed to spawn getent: %s", error->message);
-      return;
+      ide_subprocess_launcher_push_argv (launcher, user_shell);
+      if (ide_shell_supports_dash_login (user_shell))
+        ide_subprocess_launcher_push_argv (launcher, "-l");
+      ide_subprocess_launcher_push_argv (launcher, "-c");
+      ide_subprocess_launcher_push_argv (launcher, "echo $PATH");
+    }
+  else
+    {
+      ide_subprocess_launcher_push_args (launcher,
+                                         IDE_STRV_INIT ("/bin/sh", "-l", "-c", "echo $PATH"));
     }
 
-  ide_subprocess_communicate_utf8_async (subprocess,
-                                         NULL,
-                                         NULL,
-                                         ide_guess_shell_communicate_cb,
-                                         (gpointer)"PATH");
+  if (!(subprocess = ide_subprocess_launcher_spawn (launcher, NULL, &error)))
+    ide_task_return_error (task, g_steal_pointer (&error));
+  else
+    ide_subprocess_communicate_utf8_async (subprocess,
+                                           NULL,
+                                           NULL,
+                                           ide_guess_shell_communicate_cb,
+                                           g_steal_pointer (&task));
+
+  IDE_EXIT;
 }
 
 /**
@@ -227,3 +264,64 @@ ide_get_user_default_path (void)
 {
   return user_default_path;
 }
+
+static void
+ide_shell_init_guess_path_cb (GObject      *object,
+                              GAsyncResult *result,
+                              gpointer      user_data)
+{
+  g_autoptr(GError) error = NULL;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (object == NULL);
+  g_assert (IDE_IS_TASK (result));
+  g_assert (user_data == NULL);
+
+  if (!ide_task_propagate_boolean (IDE_TASK (result), &error))
+    g_warning ("Failed to guess user $PATH using $SHELL %s: %s",
+               user_shell, error->message);
+
+  IDE_EXIT;
+}
+
+static void
+ide_shell_init_guess_shell_cb (GObject      *object,
+                               GAsyncResult *result,
+                               gpointer      user_data)
+{
+  g_autoptr(GError) error = NULL;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (object == NULL);
+  g_assert (IDE_IS_TASK (result));
+  g_assert (user_data == NULL);
+
+  if (!ide_task_propagate_boolean (IDE_TASK (result), &error))
+    g_warning ("Failed to guess user $SHELL: %s", error->message);
+
+  _ide_guess_user_path (NULL,
+                        ide_shell_init_guess_path_cb,
+                        NULL);
+
+  IDE_EXIT;
+}
+
+void
+_ide_shell_init (void)
+{
+  IDE_ENTRY;
+
+  /* First we need to guess the user shell, so that we can potentially
+   * get the path using that shell (instead of just /bin/sh which might
+   * not include things like .bashrc).
+   */
+  _ide_guess_shell (NULL,
+                    ide_shell_init_guess_shell_cb,
+                    NULL);
+
+  IDE_EXIT;
+}
diff --git a/src/main.c b/src/main.c
index 6f2495bcd..7a4b77282 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,6 +1,6 @@
 /* main.c
  *
- * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ * Copyright 2018-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
@@ -298,8 +298,7 @@ main (gint   argc,
   _ide_thread_pool_init (FALSE);
 
   /* Guess the user $SHELL and $PATH early */
-  _ide_guess_shell ();
-  _ide_guess_user_path ();
+  _ide_shell_init ();
 
   /* Ensure availability of some symbols possibly dropped in link */
   _ide_tweaks_init ();


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