watchman/UserDir.cpp (123 lines of code) (raw):
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "watchman/UserDir.h"
#include <folly/String.h>
#include "watchman/Logging.h"
#include "watchman/Options.h"
#include "watchman/fs/FileSystem.h"
#ifdef _WIN32
#include <Lmcons.h> // @manual
#include <Shlobj.h> // @manual
#endif
namespace watchman {
namespace {
const char*
getEnvWithFallback(const char* name1, const char* name2, const char* fallback) {
const char* val = getenv(name1);
if (!val || *val == 0) {
val = getenv(name2);
}
if (!val || *val == 0) {
val = fallback;
}
return val;
}
#ifdef _WIN32
std::string getWatchmanAppDataPath() {
PWSTR local_app_data = nullptr;
auto res =
SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &local_app_data);
if (res != S_OK) {
logf(
FATAL,
"SHGetKnownFolderPath FOLDERID_LocalAppData failed: {}\n",
win32_strerror(res));
}
SCOPE_EXIT {
CoTaskMemFree(local_app_data);
};
// Perform path mapping from wide string to our preferred UTF8
w_string temp_location(local_app_data, wcslen(local_app_data));
// and use the watchman subdir of LOCALAPPDATA
auto watchmanDir = folly::to<std::string>(temp_location.view(), "/watchman");
if (mkdir(watchmanDir.c_str(), 0700) == 0 || errno == EEXIST) {
return watchmanDir;
}
logf(
ERR,
"failed to create directory {}: {}\n",
watchmanDir,
folly::errnoStr(errno));
exit(1);
}
const std::string& getCachedWatchmanAppDataPath() {
static std::string path = getWatchmanAppDataPath();
return path;
}
#endif
std::string computeTemporaryDirectory() {
#ifdef _WIN32
if (!flags.test_state_dir.empty()) {
return flags.test_state_dir;
} else {
return getCachedWatchmanAppDataPath();
}
#else
return getEnvWithFallback("TMPDIR", "TMP", "/tmp");
#endif
}
} // namespace
std::string computeUserName() {
#ifdef _WIN32
// We don't trust the environment on Win32 because in some situations
// the environment may contain the domain name like `WORKGROUP\user`
// which can confuse some path construction we do later on.
WCHAR userW[1 + UNLEN];
DWORD size = static_cast<DWORD>(std::size(userW));
if (GetUserNameW(userW, &size) && size > 0) {
// Constructing a w_string from a WCHAR* will convert to UTF-8
w_string user(userW, size);
return user.string();
}
DWORD lastError = GetLastError();
log(FATAL,
"GetUserName failed: ",
win32_strerror(lastError),
". I don't know who you are!?\n");
#else
const char* user = getEnvWithFallback("USER", "LOGNAME", nullptr);
if (user) {
return user;
}
uid_t uid = getuid();
struct passwd* pw = getpwuid(uid);
if (!pw) {
log(FATAL,
"getpwuid(",
uid,
") failed: ",
folly::errnoStr(errno),
". I don't know who you are\n");
}
user = pw->pw_name;
if (user) {
return user;
}
log(FATAL, "watchman requires that you set $USER in your env\n");
#endif
throw std::logic_error("unreachable");
}
const std::string& getTemporaryDirectory() {
static std::string tmpdir = computeTemporaryDirectory();
return tmpdir;
}
std::string computeWatchmanStateDirectory(const std::string& user) {
if (!flags.test_state_dir.empty()) {
return folly::to<std::string>(flags.test_state_dir, "/", user, "-state");
}
#ifdef _WIN32
return getCachedWatchmanAppDataPath();
#else
auto state_parent =
#ifdef WATCHMAN_STATE_DIR
WATCHMAN_STATE_DIR
#else
getTemporaryDirectory().c_str()
#endif
;
return folly::to<std::string>(state_parent, "/", user, "-state");
#endif
}
} // namespace watchman