15ffd83dbSDimitry Andric //===-- HostInfoPosix.cpp -------------------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
90b57cec5SDimitry Andric #include "lldb/Host/posix/HostInfoPosix.h"
100b57cec5SDimitry Andric #include "lldb/Utility/Log.h"
119dba64beSDimitry Andric #include "lldb/Utility/UserIDResolver.h"
120b57cec5SDimitry Andric 
130b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h"
140b57cec5SDimitry Andric #include "llvm/ADT/Twine.h"
150b57cec5SDimitry Andric #include "llvm/Support/Path.h"
160b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
170b57cec5SDimitry Andric 
18fe6060f1SDimitry Andric #include <climits>
19fe6060f1SDimitry Andric #include <cstdlib>
200b57cec5SDimitry Andric #include <grp.h>
210b57cec5SDimitry Andric #include <mutex>
22bdd1243dSDimitry Andric #include <optional>
230b57cec5SDimitry Andric #include <pwd.h>
240b57cec5SDimitry Andric #include <sys/types.h>
25349cc55cSDimitry Andric #include <sys/utsname.h>
260b57cec5SDimitry Andric #include <unistd.h>
270b57cec5SDimitry Andric 
280b57cec5SDimitry Andric using namespace lldb_private;
290b57cec5SDimitry Andric 
GetPageSize()300b57cec5SDimitry Andric size_t HostInfoPosix::GetPageSize() { return ::getpagesize(); }
310b57cec5SDimitry Andric 
GetHostname(std::string & s)320b57cec5SDimitry Andric bool HostInfoPosix::GetHostname(std::string &s) {
330b57cec5SDimitry Andric   char hostname[PATH_MAX];
340b57cec5SDimitry Andric   hostname[sizeof(hostname) - 1] = '\0';
350b57cec5SDimitry Andric   if (::gethostname(hostname, sizeof(hostname) - 1) == 0) {
360b57cec5SDimitry Andric     s.assign(hostname);
370b57cec5SDimitry Andric     return true;
380b57cec5SDimitry Andric   }
390b57cec5SDimitry Andric   return false;
400b57cec5SDimitry Andric }
410b57cec5SDimitry Andric 
GetOSKernelDescription()42bdd1243dSDimitry Andric std::optional<std::string> HostInfoPosix::GetOSKernelDescription() {
43349cc55cSDimitry Andric   struct utsname un;
44349cc55cSDimitry Andric   if (uname(&un) < 0)
45bdd1243dSDimitry Andric     return std::nullopt;
46349cc55cSDimitry Andric 
47349cc55cSDimitry Andric   return std::string(un.version);
48349cc55cSDimitry Andric }
49349cc55cSDimitry Andric 
500b57cec5SDimitry Andric #ifdef __ANDROID__
510b57cec5SDimitry Andric #include <android/api-level.h>
520b57cec5SDimitry Andric #endif
530b57cec5SDimitry Andric #if defined(__ANDROID_API__) && __ANDROID_API__ < 21
540b57cec5SDimitry Andric #define USE_GETPWUID
550b57cec5SDimitry Andric #endif
560b57cec5SDimitry Andric 
570b57cec5SDimitry Andric namespace {
580b57cec5SDimitry Andric class PosixUserIDResolver : public UserIDResolver {
590b57cec5SDimitry Andric protected:
60bdd1243dSDimitry Andric   std::optional<std::string> DoGetUserName(id_t uid) override;
61bdd1243dSDimitry Andric   std::optional<std::string> DoGetGroupName(id_t gid) override;
620b57cec5SDimitry Andric };
630b57cec5SDimitry Andric } // namespace
640b57cec5SDimitry Andric 
659dba64beSDimitry Andric struct PasswdEntry {
669dba64beSDimitry Andric   std::string username;
679dba64beSDimitry Andric   std::string shell;
689dba64beSDimitry Andric };
699dba64beSDimitry Andric 
GetPassword(id_t uid)70bdd1243dSDimitry Andric static std::optional<PasswdEntry> GetPassword(id_t uid) {
710b57cec5SDimitry Andric #ifdef USE_GETPWUID
720b57cec5SDimitry Andric   // getpwuid_r is missing from android-9
739dba64beSDimitry Andric   // The caller should provide some thread safety by making sure no one calls
749dba64beSDimitry Andric   // this function concurrently, because using getpwuid is ultimately not
759dba64beSDimitry Andric   // thread-safe as we don't know who else might be calling it.
769dba64beSDimitry Andric   if (auto *user_info_ptr = ::getpwuid(uid))
779dba64beSDimitry Andric     return PasswdEntry{user_info_ptr->pw_name, user_info_ptr->pw_shell};
780b57cec5SDimitry Andric #else
790b57cec5SDimitry Andric   struct passwd user_info;
800b57cec5SDimitry Andric   struct passwd *user_info_ptr = &user_info;
810b57cec5SDimitry Andric   char user_buffer[PATH_MAX];
820b57cec5SDimitry Andric   size_t user_buffer_size = sizeof(user_buffer);
830b57cec5SDimitry Andric   if (::getpwuid_r(uid, &user_info, user_buffer, user_buffer_size,
840b57cec5SDimitry Andric                    &user_info_ptr) == 0 &&
850b57cec5SDimitry Andric       user_info_ptr) {
869dba64beSDimitry Andric     return PasswdEntry{user_info_ptr->pw_name, user_info_ptr->pw_shell};
870b57cec5SDimitry Andric   }
880b57cec5SDimitry Andric #endif
89bdd1243dSDimitry Andric   return std::nullopt;
900b57cec5SDimitry Andric }
910b57cec5SDimitry Andric 
DoGetUserName(id_t uid)92bdd1243dSDimitry Andric std::optional<std::string> PosixUserIDResolver::DoGetUserName(id_t uid) {
93bdd1243dSDimitry Andric   if (std::optional<PasswdEntry> password = GetPassword(uid))
949dba64beSDimitry Andric     return password->username;
95bdd1243dSDimitry Andric   return std::nullopt;
969dba64beSDimitry Andric }
979dba64beSDimitry Andric 
DoGetGroupName(id_t gid)98bdd1243dSDimitry Andric std::optional<std::string> PosixUserIDResolver::DoGetGroupName(id_t gid) {
990b57cec5SDimitry Andric #ifndef __ANDROID__
1000b57cec5SDimitry Andric   char group_buffer[PATH_MAX];
1010b57cec5SDimitry Andric   size_t group_buffer_size = sizeof(group_buffer);
1020b57cec5SDimitry Andric   struct group group_info;
1030b57cec5SDimitry Andric   struct group *group_info_ptr = &group_info;
1040b57cec5SDimitry Andric   // Try the threadsafe version first
1050b57cec5SDimitry Andric   if (::getgrgid_r(gid, &group_info, group_buffer, group_buffer_size,
1060b57cec5SDimitry Andric                    &group_info_ptr) == 0) {
1070b57cec5SDimitry Andric     if (group_info_ptr)
1080b57cec5SDimitry Andric       return std::string(group_info_ptr->gr_name);
1090b57cec5SDimitry Andric   } else {
1100b57cec5SDimitry Andric     // The threadsafe version isn't currently working for me on darwin, but the
1110b57cec5SDimitry Andric     // non-threadsafe version is, so I am calling it below.
1120b57cec5SDimitry Andric     group_info_ptr = ::getgrgid(gid);
1130b57cec5SDimitry Andric     if (group_info_ptr)
1140b57cec5SDimitry Andric       return std::string(group_info_ptr->gr_name);
1150b57cec5SDimitry Andric   }
1160b57cec5SDimitry Andric #endif
117bdd1243dSDimitry Andric   return std::nullopt;
1180b57cec5SDimitry Andric }
1190b57cec5SDimitry Andric 
1200b57cec5SDimitry Andric static llvm::ManagedStatic<PosixUserIDResolver> g_user_id_resolver;
1210b57cec5SDimitry Andric 
GetUserIDResolver()1220b57cec5SDimitry Andric UserIDResolver &HostInfoPosix::GetUserIDResolver() {
1230b57cec5SDimitry Andric   return *g_user_id_resolver;
1240b57cec5SDimitry Andric }
1250b57cec5SDimitry Andric 
GetUserID()1260b57cec5SDimitry Andric uint32_t HostInfoPosix::GetUserID() { return getuid(); }
1270b57cec5SDimitry Andric 
GetGroupID()1280b57cec5SDimitry Andric uint32_t HostInfoPosix::GetGroupID() { return getgid(); }
1290b57cec5SDimitry Andric 
GetEffectiveUserID()1300b57cec5SDimitry Andric uint32_t HostInfoPosix::GetEffectiveUserID() { return geteuid(); }
1310b57cec5SDimitry Andric 
GetEffectiveGroupID()1320b57cec5SDimitry Andric uint32_t HostInfoPosix::GetEffectiveGroupID() { return getegid(); }
1330b57cec5SDimitry Andric 
GetDefaultShell()1349dba64beSDimitry Andric FileSpec HostInfoPosix::GetDefaultShell() {
1359dba64beSDimitry Andric   if (const char *v = ::getenv("SHELL"))
1369dba64beSDimitry Andric     return FileSpec(v);
137bdd1243dSDimitry Andric   if (std::optional<PasswdEntry> password = GetPassword(::geteuid()))
1389dba64beSDimitry Andric     return FileSpec(password->shell);
1399dba64beSDimitry Andric   return FileSpec("/bin/sh");
1409dba64beSDimitry Andric }
1410b57cec5SDimitry Andric 
ComputeSupportExeDirectory(FileSpec & file_spec)1420b57cec5SDimitry Andric bool HostInfoPosix::ComputeSupportExeDirectory(FileSpec &file_spec) {
1430b57cec5SDimitry Andric   return ComputePathRelativeToLibrary(file_spec, "/bin");
1440b57cec5SDimitry Andric }
1450b57cec5SDimitry Andric 
ComputeHeaderDirectory(FileSpec & file_spec)1460b57cec5SDimitry Andric bool HostInfoPosix::ComputeHeaderDirectory(FileSpec &file_spec) {
1470b57cec5SDimitry Andric   FileSpec temp_file("/opt/local/include/lldb");
148bdd1243dSDimitry Andric   file_spec.SetDirectory(temp_file.GetPath());
1490b57cec5SDimitry Andric   return true;
1500b57cec5SDimitry Andric }
1510b57cec5SDimitry Andric 
GetEnvironmentVar(const std::string & var_name,std::string & var)1520b57cec5SDimitry Andric bool HostInfoPosix::GetEnvironmentVar(const std::string &var_name,
1530b57cec5SDimitry Andric                                       std::string &var) {
1540b57cec5SDimitry Andric   if (const char *pvar = ::getenv(var_name.c_str())) {
1550b57cec5SDimitry Andric     var = std::string(pvar);
1560b57cec5SDimitry Andric     return true;
1570b57cec5SDimitry Andric   }
1580b57cec5SDimitry Andric   return false;
1590b57cec5SDimitry Andric }
160