blob: c575b3d43139b86c4f0f22d58949a1c57f49894e [file] [log] [blame]
// Copyright (C) 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "android/base/system/System.h"
#include "android/base/EintrWrapper.h"
#include "android/base/files/PathUtils.h"
#include "android/base/memory/LazyInstance.h"
#include "android/base/misc/StringUtils.h"
#include "android/base/StringFormat.h"
#ifdef _WIN32
#include <windows.h>
#endif
#ifdef __APPLE__
#import <Carbon/Carbon.h>
#endif // __APPLE__
#ifndef _WIN32
#include <dirent.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
namespace android {
namespace base {
namespace {
class HostSystem : public System {
public:
HostSystem() : mProgramDir() {}
virtual ~HostSystem() {}
virtual const String& getProgramDirectory() const {
if (mProgramDir.empty()) {
#if defined(__linux__)
char path[1024];
int len = readlink("/proc/self/exe", path, sizeof(path));
if (len > 0 && len < (int)sizeof(path)) {
char* x = ::strrchr(path, '/');
if (x) {
*x = '\0';
mProgramDir.assign(path);
}
}
#elif defined(__APPLE__)
ProcessSerialNumber psn;
GetCurrentProcess(&psn);
CFDictionaryRef dict =
ProcessInformationCopyDictionary(&psn, 0xffffffff);
CFStringRef value = (CFStringRef)CFDictionaryGetValue(
dict, CFSTR("CFBundleExecutable"));
char s[PATH_MAX];
CFStringGetCString(value, s, PATH_MAX - 1, kCFStringEncodingUTF8);
char* x = ::strrchr(s, '/');
if (x) {
*x = '\0';
mProgramDir.assign(s);
} else {
mProgramDir.assign("<unknown-application-dir>");
}
#elif defined(_WIN32)
char appDir[MAX_PATH];
int len = GetModuleFileName(0, appDir, sizeof(appDir)-1);
mProgramDir.assign("<unknown-application-dir>");
if (len > 0 && len < (int)sizeof(appDir)) {
appDir[len] = 0;
char* sep = ::strrchr(appDir, '\\');
if (sep) {
*sep = '\0';
mProgramDir.assign(appDir);
}
}
#else
#error "Unsupported platform!"
#endif
}
return mProgramDir;
}
virtual int getHostBitness() const {
#ifdef _WIN32
char directory[900];
// Retrieves the path of the WOW64 system directory, which doesn't
// exist on 32-bit systems.
unsigned len = GetSystemWow64Directory(directory, sizeof(directory));
if (len == 0) {
return 32;
} else {
return 64;
}
#else // !_WIN32
/*
This function returns 64 if host is running 64-bit OS, or 32 otherwise.
It uses the same technique in ndk/build/core/ndk-common.sh.
Here are comments from there:
## On Linux or Darwin, a 64-bit kernel (*) doesn't mean that the
## user-landis always 32-bit, so use "file" to determine the bitness
## of the shell that invoked us. The -L option is used to de-reference
## symlinks.
##
## Note that on Darwin, a single executable can contain both x86 and
## x86_64 machine code, so just look for x86_64 (darwin) or x86-64
## (Linux) in the output.
(*) ie. The following code doesn't always work:
struct utsname u;
int host_runs_64bit_OS = (uname(&u) == 0 &&
strcmp(u.machine, "x86_64") == 0);
*/
return system("file -L \"$SHELL\" | grep -q \"x86[_-]64\"") == 0 ?
64 : 32;
#endif // !_WIN32
}
virtual StringVector scanDirEntries(const char* dirPath,
bool fullPath = false) {
StringVector result = scanDirInternal(dirPath);
if (fullPath) {
// Pre-pend |dirPath| to each entry.
String prefix =
PathUtils::addTrailingDirSeparator(String(dirPath));
for (size_t n = 0; n < result.size(); ++n) {
String path = prefix;
path.append(result[n]);
result[n] = path;
}
}
return result;
}
virtual const char* envGet(const char* varname) const {
return getenv(varname);
}
virtual void envSet(const char* varname, const char* varvalue) {
#ifdef _WIN32
if (!varvalue || !varvalue[0]) {
varvalue = "";
}
size_t length = ::strlen(varname) + ::strlen(varvalue) + 2;
char* string = static_cast<char*>(malloc(length));
snprintf(string, length, "%s=%s", varname, varvalue);
putenv(string);
#else
if (!varvalue || !varvalue[0]) {
unsetenv(varname);
} else {
setenv(varname, varvalue, 1);
}
#endif
}
virtual bool isRemoteSession(String* sessionType) const {
if (getenv("NX_TEMP") != NULL) {
if (sessionType) {
*sessionType = "NX";
}
return true;
}
if (getenv("CHROME_REMOTE_DESKTOP_SESSION") != NULL) {
if (sessionType) {
*sessionType = "Chrome Remote Desktop";
}
return true;
}
#ifdef _WIN32
if (GetSystemMetrics(SM_REMOTESESSION)) {
if (sessionType) {
*sessionType = "Windows Remote Desktop";
}
return true;
}
#endif // _WIN32
return false;
}
virtual bool pathExists(const char* path) {
return pathExistsInternal(path);
}
virtual bool pathIsFile(const char* path) {
return pathIsFileInternal(path);
}
virtual bool pathIsDir(const char* path) {
return pathIsDirInternal(path);
}
private:
mutable String mProgramDir;
};
LazyInstance<HostSystem> sHostSystem = LAZY_INSTANCE_INIT;
System* sSystemForTesting = NULL;
} // namespace
// static
System* System::get() {
System* result = sSystemForTesting;
if (!result) {
result = sHostSystem.ptr();
}
return result;
}
#ifdef __x86_64__
// static
const char* System::kLibSubDir = "lib64";
#else
// static
const char* System::kLibSubDir = "lib";
#endif
#ifdef _WIN32
// static
const char* System::kLibrarySearchListEnvVarName = "PATH";
#elif defined(__APPLE__)
const char* System::kLibrarySearchListEnvVarName = "DYLD_LIBRARY_PATH";
#else
// static
const char* System::kLibrarySearchListEnvVarName = "LD_LIBRARY_PATH";
#endif
// static
System* System::setForTesting(System* system) {
System* result = sSystemForTesting;
sSystemForTesting = system;
return result;
}
// static
StringVector System::scanDirInternal(const char* dirPath) {
StringVector result;
if (!dirPath) {
return result;
}
#ifdef _WIN32
String root(dirPath);
root = PathUtils::addTrailingDirSeparator(root);
root += '*';
struct _finddata_t findData;
intptr_t findIndex = _findfirst(root.c_str(), &findData);
if (findIndex >= 0) {
do {
const char* name = findData.name;
if (strcmp(name, ".") != 0 && strcmp(name, "..") != 0) {
result.append(String(name));
}
} while (_findnext(findIndex, &findData) >= 0);
_findclose(findIndex);
}
#else // !_WIN32
DIR* dir = ::opendir(dirPath);
if (dir) {
for (;;) {
struct dirent* entry = ::readdir(dir);
if (!entry) {
break;
}
const char* name = entry->d_name;
if (strcmp(name, ".") != 0 && strcmp(name, "..") != 0) {
result.append(String(name));
}
}
::closedir(dir);
}
#endif // !_WIN32
sortStringVector(&result);
return result;
}
// static
bool System::pathExistsInternal(const char* path) {
if (!path) {
return false;
}
int ret = HANDLE_EINTR(access(path, F_OK));
return (ret == 0) || (errno != ENOENT);
}
// static
bool System::pathIsFileInternal(const char* path) {
if (!path) {
return false;
}
struct stat st;
int ret = HANDLE_EINTR(stat(path, &st));
if (ret < 0) {
return false;
}
return S_ISREG(st.st_mode);
}
// static
bool System::pathIsDirInternal(const char* path) {
if (!path) {
return false;
}
struct stat st;
int ret = HANDLE_EINTR(stat(path, &st));
if (ret < 0) {
return false;
}
return S_ISDIR(st.st_mode);
}
// static
void System::addLibrarySearchDir(const char* path) {
System* system = System::get();
const char* varName = kLibrarySearchListEnvVarName;
const char* env = system->envGet(varName);
String libSearchPath = env ? env : "";
if (libSearchPath.size()) {
libSearchPath = StringFormat("%s%c%s",
path,
kPathSeparator,
libSearchPath.c_str());
} else {
libSearchPath = path;
}
system->envSet(varName, libSearchPath.c_str());
}
} // namespace base
} // namespace android