[Host] Return the user's shell from GetDefaultShell
authorJonas Devlieghere <jonas@devlieghere.com>
Thu, 3 Oct 2019 18:29:01 +0000 (18:29 +0000)
committerJonas Devlieghere <jonas@devlieghere.com>
Thu, 3 Oct 2019 18:29:01 +0000 (18:29 +0000)
LLDB handles shell expansion by running lldb-argdumper under a shell.
Currently, this is always /bin/sh on POSIX. This potentially leads to
different behavior between lldb and the user's current shell. Here's an
example of different expansions between shells:

$ /bin/bash -c 'echo -config={Options:[{key:foo_key,value:foo_value}]}'
-config={Options:[key:foo_key]} -config={Options:[value:foo_value]}

$ /bin/zsh -c 'echo -config={Options:[{key:foo_key,value:foo_value}]}'
zsh:1: no matches found: -config={Options:[key:foo_key]}

$ /bin/sh -c 'echo -config={Options:[{key:foo_key,value:foo_value}]}'
-config={Options:[key:foo_key]} -config={Options:[value:foo_value]}

$ /bin/fish -c 'echo -config={Options:[{key:foo_key,value:foo_value}]}'
-config=Options:[key:foo_key] -config=Options:[value:foo_value]

To reduce surprises, this patch returns the user's current shell. It
first looks at the SHELL environment variable. If that isn't set, it'll
ask for the user's default shell. Only if that fails, we'll fallback to
/bin/sh, which should always be available.

Differential revision: https://reviews.llvm.org/D68316

llvm-svn: 373644

lldb/lit/Host/Inputs/simple.c [new file with mode: 0644]
lldb/lit/Host/TestCustomShell.test [new file with mode: 0644]
lldb/source/Host/posix/HostInfoPosix.cpp

diff --git a/lldb/lit/Host/Inputs/simple.c b/lldb/lit/Host/Inputs/simple.c
new file mode 100644 (file)
index 0000000..06e216a
--- /dev/null
@@ -0,0 +1 @@
+int main(int argc, char const *argv[]) { return 0; }
diff --git a/lldb/lit/Host/TestCustomShell.test b/lldb/lit/Host/TestCustomShell.test
new file mode 100644 (file)
index 0000000..58cda2e
--- /dev/null
@@ -0,0 +1,8 @@
+# UNSUPPORTED: system-windows
+
+# RUN: %clang %S/Inputs/simple.c -g -o %t.out
+# RUN: SHELL=bogus %lldb %t.out -b -o 'run' 2>&1 | FileCheck %s --check-prefix ERROR
+# RUN: env -i %lldb %t.out -b -o 'run' 2>&1 | FileCheck %s
+
+# ERROR: error: shell expansion failed
+# CHECK-NOT: error: shell expansion failed
index e7d0dee..76f77e3 100644 (file)
@@ -52,15 +52,19 @@ protected:
 };
 } // namespace
 
-llvm::Optional<std::string> PosixUserIDResolver::DoGetUserName(id_t uid) {
+struct PasswdEntry {
+  std::string username;
+  std::string shell;
+};
+
+static llvm::Optional<PasswdEntry> GetPassword(id_t uid) {
 #ifdef USE_GETPWUID
   // getpwuid_r is missing from android-9
-  // UserIDResolver provides some thread safety by making sure noone calls this
-  // function concurrently, but using getpwuid is ultimately not thread-safe as
-  // we don't know who else might be calling it.
-  struct passwd *user_info_ptr = ::getpwuid(uid);
-  if (user_info_ptr)
-    return std::string(user_info_ptr->pw_name);
+  // The caller should provide some thread safety by making sure no one calls
+  // this function concurrently, because using getpwuid is ultimately not
+  // thread-safe as we don't know who else might be calling it.
+  if (auto *user_info_ptr = ::getpwuid(uid))
+    return PasswdEntry{user_info_ptr->pw_name, user_info_ptr->pw_shell};
 #else
   struct passwd user_info;
   struct passwd *user_info_ptr = &user_info;
@@ -69,12 +73,18 @@ llvm::Optional<std::string> PosixUserIDResolver::DoGetUserName(id_t uid) {
   if (::getpwuid_r(uid, &user_info, user_buffer, user_buffer_size,
                    &user_info_ptr) == 0 &&
       user_info_ptr) {
-    return std::string(user_info_ptr->pw_name);
+    return PasswdEntry{user_info_ptr->pw_name, user_info_ptr->pw_shell};
   }
 #endif
   return llvm::None;
 }
 
+llvm::Optional<std::string> PosixUserIDResolver::DoGetUserName(id_t uid) {
+  if (llvm::Optional<PasswdEntry> password = GetPassword(uid))
+    return password->username;
+  return llvm::None;
+}
+
 llvm::Optional<std::string> PosixUserIDResolver::DoGetGroupName(id_t gid) {
 #ifndef __ANDROID__
   char group_buffer[PATH_MAX];
@@ -113,7 +123,13 @@ uint32_t HostInfoPosix::GetEffectiveUserID() { return geteuid(); }
 
 uint32_t HostInfoPosix::GetEffectiveGroupID() { return getegid(); }
 
-FileSpec HostInfoPosix::GetDefaultShell() { return FileSpec("/bin/sh"); }
+FileSpec HostInfoPosix::GetDefaultShell() {
+  if (const char *v = ::getenv("SHELL"))
+    return FileSpec(v);
+  if (llvm::Optional<PasswdEntry> password = GetPassword(::geteuid()))
+    return FileSpec(password->shell);
+  return FileSpec("/bin/sh");
+}
 
 bool HostInfoPosix::ComputeSupportExeDirectory(FileSpec &file_spec) {
   return ComputePathRelativeToLibrary(file_spec, "/bin");