[vm] Remove bin/*_android{.cc,.h} and use Linux implementations.

Android is based on Linux, so most of the files were identical
sans some subtle discrepancies caused by drift over time.

This discrepancies were making code base harder to maintain and in fact
were hiding bugs. For example, on Android eventhandler's implementation
of timers which relied on passing timeout to `epoll_wait` contained
a bug which was not present on Linux which used `timerfd` instead.

TEST=ci and manual testing of Flutter app on Android device

Fixes https://github.com/dart-lang/sdk/issues/54868

Cq-Include-Trybots: luci.dart.try:vm-aot-android-release-arm64c-try,vm-aot-android-release-arm_x64-try,vm-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64c-try,vm-ffi-android-product-arm-try,vm-ffi-android-product-arm64c-try,vm-ffi-android-release-arm-try,vm-ffi-android-release-arm64c-try
Bug: b/311165013
Change-Id: Ia166f69c14177ec34160805a0983eafee8ea65f6
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/350923
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Slava Egorov <vegorov@google.com>
This commit is contained in:
Vyacheslav Egorov 2024-02-09 14:10:49 +00:00 committed by Commit Queue
parent 2564dc0b89
commit 14f7622ef4
46 changed files with 139 additions and 5390 deletions

View file

@ -9,7 +9,6 @@
builtin_impl_sources = [
"crypto.cc",
"crypto.h",
"crypto_android.cc",
"crypto_fuchsia.cc",
"crypto_linux.cc",
"crypto_macos.cc",
@ -18,7 +17,6 @@ builtin_impl_sources = [
"dartutils.h",
"directory.cc",
"directory.h",
"directory_android.cc",
"directory_fuchsia.cc",
"directory_linux.cc",
"directory_macos.cc",
@ -26,13 +24,11 @@ builtin_impl_sources = [
"exe_utils.cc",
"exe_utils.h",
"fdutils.h",
"fdutils_android.cc",
"fdutils_fuchsia.cc",
"fdutils_linux.cc",
"fdutils_macos.cc",
"file.cc",
"file.h",
"file_android.cc",
"file_fuchsia.cc",
"file_linux.cc",
"file_macos.cc",
@ -47,8 +43,6 @@ builtin_impl_sources = [
"thread.h",
"thread_absl.cc",
"thread_absl.h",
"thread_android.cc",
"thread_android.h",
"thread_fuchsia.cc",
"thread_fuchsia.h",
"thread_linux.cc",
@ -59,7 +53,6 @@ builtin_impl_sources = [
"thread_win.h",
"utils.cc",
"utils.h",
"utils_android.cc",
"utils_fuchsia.cc",
"utils_linux.cc",
"utils_macos.cc",

View file

@ -1,44 +0,0 @@
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h"
#if defined(DART_HOST_OS_ANDROID)
#include <errno.h> // NOLINT
#include <fcntl.h> // NOLINT
#include "bin/crypto.h"
#include "bin/fdutils.h"
#include "platform/signal_blocker.h"
namespace dart {
namespace bin {
bool Crypto::GetRandomBytes(intptr_t count, uint8_t* buffer) {
ThreadSignalBlocker signal_blocker(SIGPROF);
intptr_t fd = TEMP_FAILURE_RETRY_NO_SIGNAL_BLOCKER(
open("/dev/urandom", O_RDONLY | O_CLOEXEC));
if (fd < 0) {
return false;
}
intptr_t bytes_read = 0;
do {
int res = TEMP_FAILURE_RETRY_NO_SIGNAL_BLOCKER(
read(fd, buffer + bytes_read, count - bytes_read));
if (res < 0) {
int err = errno;
close(fd);
errno = err;
return false;
}
bytes_read += res;
} while (bytes_read < count);
close(fd);
return true;
}
} // namespace bin
} // namespace dart
#endif // defined(DART_HOST_OS_ANDROID)

View file

@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h"
#if defined(DART_HOST_OS_LINUX)
#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
#include <errno.h> // NOLINT
#include <fcntl.h> // NOLINT
@ -41,4 +41,4 @@ bool Crypto::GetRandomBytes(intptr_t count, uint8_t* buffer) {
} // namespace bin
} // namespace dart
#endif // defined(DART_HOST_OS_LINUX)
#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)

View file

@ -792,19 +792,40 @@ Dart_Handle DartUtils::NewInternalError(const char* message) {
}
Dart_Handle DartUtils::NewStringFormatted(const char* format, ...) {
va_list args;
va_start(args, format);
char* result = ScopedCStringVFormatted(format, args);
va_end(args);
return NewString(result);
}
char* DartUtils::ScopedCStringVFormatted(const char* format, va_list args) {
va_list measure_args;
va_start(measure_args, format);
va_copy(measure_args, args);
intptr_t len = vsnprintf(nullptr, 0, format, measure_args);
if (len < 0) {
return nullptr;
}
va_end(measure_args);
char* buffer = reinterpret_cast<char*>(Dart_ScopeAllocate(len + 1));
char* buffer = ScopedCString(len + 1);
MSAN_UNPOISON(buffer, (len + 1));
va_list print_args;
va_start(print_args, format);
vsnprintf(buffer, (len + 1), format, print_args);
va_copy(print_args, args);
len = vsnprintf(buffer, (len + 1), format, print_args);
if (len < 0) {
return nullptr;
}
va_end(print_args);
return buffer;
}
return NewString(buffer);
char* DartUtils::ScopedCStringFormatted(const char* format, ...) {
va_list args;
va_start(args, format);
char* result = ScopedCStringVFormatted(format, args);
va_end(args);
return result;
}
bool DartUtils::SetOriginalWorkingDirectory() {

View file

@ -228,6 +228,10 @@ class DartUtils {
return result;
}
static char* ScopedCStringFormatted(const char* format, ...)
PRINTF_ATTRIBUTE(1, 2);
static char* ScopedCStringVFormatted(const char* format, va_list args);
// Create a new Dart InternalError object with the provided message.
static Dart_Handle NewError(const char* format, ...);
static Dart_Handle NewInternalError(const char* message);

View file

@ -1,512 +0,0 @@
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h"
#if defined(DART_HOST_OS_ANDROID)
#include "bin/directory.h"
#include <dirent.h> // NOLINT
#include <errno.h> // NOLINT
#include <fcntl.h> // NOLINT
#include <stdlib.h> // NOLINT
#include <string.h> // NOLINT
#include <sys/param.h> // NOLINT
#include <sys/stat.h> // NOLINT
#include <unistd.h> // NOLINT
#include "bin/crypto.h"
#include "bin/dartutils.h"
#include "bin/fdutils.h"
#include "bin/file.h"
#include "bin/namespace.h"
#include "bin/platform.h"
#include "platform/signal_blocker.h"
namespace dart {
namespace bin {
PathBuffer::PathBuffer() : length_(0) {
data_ = calloc(PATH_MAX + 1, sizeof(char)); // NOLINT
}
PathBuffer::~PathBuffer() {
free(data_);
}
bool PathBuffer::AddW(const wchar_t* name) {
UNREACHABLE();
return false;
}
char* PathBuffer::AsString() const {
return reinterpret_cast<char*>(data_);
}
wchar_t* PathBuffer::AsStringW() const {
UNREACHABLE();
return nullptr;
}
const char* PathBuffer::AsScopedString() const {
return DartUtils::ScopedCopyCString(AsString());
}
bool PathBuffer::Add(const char* name) {
char* data = AsString();
int written = snprintf(data + length_, PATH_MAX - length_, "%s", name);
data[PATH_MAX] = '\0';
if ((written <= PATH_MAX - length_) && (written >= 0) &&
(static_cast<size_t>(written) == strnlen(name, PATH_MAX + 1))) {
length_ += written;
return true;
} else {
errno = ENAMETOOLONG;
return false;
}
}
void PathBuffer::Reset(intptr_t new_length) {
length_ = new_length;
AsString()[length_] = '\0';
}
// A linked list of symbolic links, with their unique file system identifiers.
// These are scanned to detect loops while doing a recursive directory listing.
struct LinkList {
uint64_t dev;
uint64_t ino;
LinkList* next;
};
ListType DirectoryListingEntry::Next(DirectoryListing* listing) {
if (done_) {
return kListDone;
}
if (fd_ == -1) {
ASSERT(lister_ == 0);
NamespaceScope ns(listing->namespc(), listing->path_buffer().AsString());
const int listingfd =
TEMP_FAILURE_RETRY(openat(ns.fd(), ns.path(), O_DIRECTORY));
if (listingfd < 0) {
done_ = true;
return kListError;
}
fd_ = listingfd;
}
if (lister_ == 0) {
do {
lister_ = reinterpret_cast<intptr_t>(fdopendir(fd_));
} while ((lister_ == 0) && (errno == EINTR));
if (lister_ == 0) {
done_ = true;
return kListError;
}
if (parent_ != nullptr) {
if (!listing->path_buffer().Add(File::PathSeparator())) {
return kListError;
}
}
path_length_ = listing->path_buffer().length();
}
// Reset.
listing->path_buffer().Reset(path_length_);
ResetLink();
// Iterate the directory and post the directories and files to the
// ports.
errno = 0;
dirent* entry = readdir(reinterpret_cast<DIR*>(lister_));
if (entry != nullptr) {
if (!listing->path_buffer().Add(entry->d_name)) {
done_ = true;
return kListError;
}
switch (entry->d_type) {
case DT_DIR:
if ((strcmp(entry->d_name, ".") == 0) ||
(strcmp(entry->d_name, "..") == 0)) {
return Next(listing);
}
return kListDirectory;
case DT_BLK:
case DT_CHR:
case DT_FIFO:
case DT_SOCK:
case DT_REG:
return kListFile;
case DT_LNK:
if (!listing->follow_links()) {
return kListLink;
}
// Else fall through to next case.
FALL_THROUGH;
case DT_UNKNOWN: {
// On some file systems the entry type is not determined by
// readdir. For those and for links we use stat to determine
// the actual entry type. Notice that stat returns the type of
// the file pointed to.
NamespaceScope ns(listing->namespc(),
listing->path_buffer().AsString());
struct stat entry_info;
int stat_success;
stat_success = TEMP_FAILURE_RETRY(
fstatat(ns.fd(), ns.path(), &entry_info, AT_SYMLINK_NOFOLLOW));
if (stat_success == -1) {
return kListError;
}
if (listing->follow_links() && S_ISLNK(entry_info.st_mode)) {
// Check to see if we are in a loop created by a symbolic link.
LinkList current_link = {entry_info.st_dev, entry_info.st_ino, link_};
LinkList* previous = link_;
while (previous != nullptr) {
if ((previous->dev == current_link.dev) &&
(previous->ino == current_link.ino)) {
// Report the looping link as a link, rather than following it.
return kListLink;
}
previous = previous->next;
}
stat_success =
TEMP_FAILURE_RETRY(fstatat(ns.fd(), ns.path(), &entry_info, 0));
if (stat_success == -1 || (S_IFMT & entry_info.st_mode) == 0) {
// Report a broken link as a link, even if follow_links is true.
// A symbolic link can potentially point to an anon_inode. For
// example, an epoll file descriptor will have a symbolic link whose
// content is the string anon_inode:[eventpoll]. In this case, the
// target doesn't belong to any regular file category.
return kListLink;
}
if (S_ISDIR(entry_info.st_mode)) {
// Recurse into the subdirectory with current_link added to the
// linked list of seen file system links.
link_ = new LinkList(current_link);
if ((strcmp(entry->d_name, ".") == 0) ||
(strcmp(entry->d_name, "..") == 0)) {
return Next(listing);
}
return kListDirectory;
}
}
if (S_ISDIR(entry_info.st_mode)) {
if ((strcmp(entry->d_name, ".") == 0) ||
(strcmp(entry->d_name, "..") == 0)) {
return Next(listing);
}
return kListDirectory;
} else if (S_ISLNK(entry_info.st_mode)) {
return kListLink;
} else {
// Regular files, character devices, block devices, fifos, sockets and
// unknown types are all considered as files.
return kListFile;
}
}
default:
// We should have covered all the bases. If not, let's get an error.
FATAL("Unexpected d_type: %d\n", entry->d_type);
return kListError;
}
}
done_ = true;
if (errno != 0) {
return kListError;
}
return kListDone;
}
DirectoryListingEntry::~DirectoryListingEntry() {
ResetLink();
if (lister_ != 0) {
// This also closes fd_.
VOID_NO_RETRY_EXPECTED(closedir(reinterpret_cast<DIR*>(lister_)));
}
}
void DirectoryListingEntry::ResetLink() {
if ((link_ != nullptr) &&
((parent_ == nullptr) || (parent_->link_ != link_))) {
delete link_;
link_ = nullptr;
}
if (parent_ != nullptr) {
link_ = parent_->link_;
}
}
static bool DeleteRecursively(int dirfd, PathBuffer* path);
static bool DeleteFile(int dirfd, char* file_name, PathBuffer* path) {
return path->Add(file_name) &&
(NO_RETRY_EXPECTED(unlinkat(dirfd, path->AsString(), 0)) == 0);
}
static bool DeleteDir(int dirfd, char* dir_name, PathBuffer* path) {
if ((strcmp(dir_name, ".") == 0) || (strcmp(dir_name, "..") == 0)) {
return true;
}
return path->Add(dir_name) && DeleteRecursively(dirfd, path);
}
static bool DeleteRecursively(int dirfd, PathBuffer* path) {
// Do not recurse into links for deletion. Instead delete the link.
// If it's a file, delete it.
struct stat st;
if (TEMP_FAILURE_RETRY(
fstatat(dirfd, path->AsString(), &st, AT_SYMLINK_NOFOLLOW)) == -1) {
return false;
} else if (!S_ISDIR(st.st_mode)) {
return (NO_RETRY_EXPECTED(unlinkat(dirfd, path->AsString(), 0)) == 0);
}
if (!path->Add(File::PathSeparator())) {
return false;
}
// Not a link. Attempt to open as a directory and recurse into the
// directory.
const int fd =
TEMP_FAILURE_RETRY(openat(dirfd, path->AsString(), O_DIRECTORY));
if (fd < 0) {
return false;
}
DIR* dir_pointer;
do {
dir_pointer = fdopendir(fd);
} while ((dir_pointer == nullptr) && (errno == EINTR));
if (dir_pointer == nullptr) {
FDUtils::SaveErrorAndClose(fd);
return false;
}
// Iterate the directory and delete all files and directories.
int path_length = path->length();
while (true) {
// In case `readdir()` returns `nullptr` we distinguish between
// end-of-stream and error by looking if `errno` was updated.
errno = 0;
// In glibc 2.24+, readdir_r is deprecated.
// According to the man page for readdir:
// "readdir(3) is not required to be thread-safe. However, in modern
// implementations (including the glibc implementation), concurrent calls to
// readdir(3) that specify different directory streams are thread-safe."
dirent* entry = readdir(dir_pointer);
if (entry == nullptr) {
// Failed to read next directory entry.
if (errno != 0) {
break;
}
// End of directory.
int status = NO_RETRY_EXPECTED(closedir(dir_pointer));
if (status != 0) {
return false;
}
status =
NO_RETRY_EXPECTED(unlinkat(dirfd, path->AsString(), AT_REMOVEDIR));
return status == 0;
}
bool ok = false;
switch (entry->d_type) {
case DT_DIR:
ok = DeleteDir(dirfd, entry->d_name, path);
break;
case DT_BLK:
case DT_CHR:
case DT_FIFO:
case DT_SOCK:
case DT_REG:
case DT_LNK:
// Treat all links as files. This will delete the link which
// is what we want no matter if the link target is a file or a
// directory.
ok = DeleteFile(dirfd, entry->d_name, path);
break;
case DT_UNKNOWN: {
if (!path->Add(entry->d_name)) {
break;
}
// On some file systems the entry type is not determined by
// readdir. For those we use lstat to determine the entry
// type.
struct stat entry_info;
if (TEMP_FAILURE_RETRY(fstatat(dirfd, path->AsString(), &entry_info,
AT_SYMLINK_NOFOLLOW)) == -1) {
break;
}
path->Reset(path_length);
if (S_ISDIR(entry_info.st_mode)) {
ok = DeleteDir(dirfd, entry->d_name, path);
} else {
// Treat links as files. This will delete the link which is
// what we want no matter if the link target is a file or a
// directory.
ok = DeleteFile(dirfd, entry->d_name, path);
}
break;
}
default:
// We should have covered all the bases. If not, let's get an error.
FATAL("Unexpected d_type: %d\n", entry->d_type);
break;
}
if (!ok) {
break;
}
path->Reset(path_length);
}
// Only happens if an error.
ASSERT(errno != 0);
int err = errno;
VOID_NO_RETRY_EXPECTED(closedir(dir_pointer));
errno = err;
return false;
}
Directory::ExistsResult Directory::Exists(Namespace* namespc,
const char* dir_name) {
NamespaceScope ns(namespc, dir_name);
struct stat entry_info;
int success = TEMP_FAILURE_RETRY(fstatat(ns.fd(), ns.path(), &entry_info, 0));
if (success == 0) {
if (S_ISDIR(entry_info.st_mode)) {
return EXISTS;
} else {
// An OSError may be constructed based on the return value of this
// function, so set errno to something that makes sense.
errno = ENOTDIR;
return DOES_NOT_EXIST;
}
} else {
if ((errno == EACCES) || (errno == EBADF) || (errno == EFAULT) ||
(errno == ENOMEM) || (errno == EOVERFLOW)) {
// Search permissions denied for one of the directories in the
// path or a low level error occurred. We do not know if the
// directory exists.
return UNKNOWN;
}
ASSERT((errno == ELOOP) || (errno == ENAMETOOLONG) || (errno == ENOENT) ||
(errno == ENOTDIR));
return DOES_NOT_EXIST;
}
}
char* Directory::CurrentNoScope() {
// Android's getcwd adheres closely to the POSIX standard. It won't
// allocate memory. We need to make our own copy.
char buffer[PATH_MAX];
if (getcwd(buffer, PATH_MAX) == nullptr) {
return nullptr;
}
return strdup(buffer);
}
bool Directory::Create(Namespace* namespc, const char* dir_name) {
NamespaceScope ns(namespc, dir_name);
// Create the directory with the permissions specified by the
// process umask.
const int result = NO_RETRY_EXPECTED(mkdirat(ns.fd(), ns.path(), 0777));
// If the directory already exists, treat it as a success.
if ((result == -1) && (errno == EEXIST)) {
return (Exists(namespc, dir_name) == EXISTS);
}
return (result == 0);
}
const char* Directory::SystemTemp(Namespace* namespc) {
if (Directory::system_temp_path_override_ != nullptr) {
return DartUtils::ScopedCopyCString(Directory::system_temp_path_override_);
}
// Android does not have a /tmp directory. A partial substitute,
// suitable for bring-up work and tests, is to create a tmp
// directory in /data/local/tmp.
//
// TODO(4413): In the long run, when running in an application we should
// probably use the appropriate directory from the Android API,
// probably what File.createTempFile uses.
const char* kAndroidTempDir = "/data/local/tmp";
struct stat st;
if (stat(kAndroidTempDir, &st) != 0) {
mkdir(kAndroidTempDir, 0777);
}
return kAndroidTempDir;
}
// Returns a new, unused directory name, adding characters to the end
// of prefix. Creates the directory with the permissions specified
// by the process umask.
// The return value is Dart_ScopeAllocated.
const char* Directory::CreateTemp(Namespace* namespc, const char* prefix) {
PathBuffer path;
const int firstchar = 'A';
const int numchars = 'Z' - 'A' + 1;
uint8_t random_bytes[7];
// mkdtemp doesn't have an "at" variant, so we have to simulate it.
if (!path.Add(prefix)) {
return nullptr;
}
intptr_t prefix_length = path.length();
while (true) {
Crypto::GetRandomBytes(6, random_bytes);
for (intptr_t i = 0; i < 6; i++) {
random_bytes[i] = (random_bytes[i] % numchars) + firstchar;
}
random_bytes[6] = '\0';
if (!path.Add(reinterpret_cast<char*>(random_bytes))) {
return nullptr;
}
NamespaceScope ns(namespc, path.AsString());
const int result = NO_RETRY_EXPECTED(mkdirat(ns.fd(), ns.path(), 0777));
if (result == 0) {
return path.AsScopedString();
} else if (errno == EEXIST) {
path.Reset(prefix_length);
} else {
return nullptr;
}
}
}
bool Directory::Delete(Namespace* namespc,
const char* dir_name,
bool recursive) {
NamespaceScope ns(namespc, dir_name);
if (!recursive) {
if ((File::GetType(namespc, dir_name, false) == File::kIsLink) &&
(File::GetType(namespc, dir_name, true) == File::kIsDirectory)) {
return NO_RETRY_EXPECTED(unlinkat(ns.fd(), ns.path(), 0)) == 0;
}
return NO_RETRY_EXPECTED(unlinkat(ns.fd(), ns.path(), AT_REMOVEDIR)) == 0;
} else {
PathBuffer path;
if (!path.Add(ns.path())) {
return false;
}
return DeleteRecursively(ns.fd(), &path);
}
}
bool Directory::Rename(Namespace* namespc,
const char* old_path,
const char* new_path) {
ExistsResult exists = Exists(namespc, old_path);
if (exists != EXISTS) {
return false;
}
NamespaceScope oldns(namespc, old_path);
NamespaceScope newns(namespc, new_path);
return (NO_RETRY_EXPECTED(renameat(oldns.fd(), oldns.path(), newns.fd(),
newns.path())) == 0);
}
} // namespace bin
} // namespace dart
#endif // defined(DART_HOST_OS_ANDROID)

View file

@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h"
#if defined(DART_HOST_OS_LINUX)
#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
#include "bin/directory.h"
@ -75,7 +75,7 @@ void PathBuffer::Reset(intptr_t new_length) {
// A linked list of symbolic links, with their unique file system identifiers.
// These are scanned to detect loops while doing a recursive directory listing.
struct LinkList {
dev_t dev;
decltype(stat64::st_dev) dev;
ino64_t ino;
LinkList* next;
};
@ -414,13 +414,21 @@ bool Directory::Create(Namespace* namespc, const char* dir_name) {
}
const char* Directory::SystemTemp(Namespace* namespc) {
if (Directory::system_temp_path_override_ != nullptr) {
return DartUtils::ScopedCopyCString(Directory::system_temp_path_override_);
}
PathBuffer path;
const char* temp_dir = getenv("TMPDIR");
if (temp_dir == nullptr) {
temp_dir = getenv("TMP");
}
if (temp_dir == nullptr) {
#if defined(DART_HOST_OS_ANDROID)
temp_dir = "/data/local/tmp";
#else
temp_dir = "/tmp";
#endif
}
NamespaceScope ns(namespc, temp_dir);
if (!path.Add(ns.path())) {
@ -507,4 +515,4 @@ bool Directory::Rename(Namespace* namespc,
} // namespace bin
} // namespace dart
#endif // defined(DART_HOST_OS_LINUX)
#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)

View file

@ -585,11 +585,9 @@ class DescriptorInfoMultipleMixin : public DI {
} // namespace dart
// The event handler delegation class is OS specific.
#if defined(DART_HOST_OS_ANDROID)
#include "bin/eventhandler_android.h"
#elif defined(DART_HOST_OS_FUCHSIA)
#if defined(DART_HOST_OS_FUCHSIA)
#include "bin/eventhandler_fuchsia.h"
#elif defined(DART_HOST_OS_LINUX)
#elif defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
#include "bin/eventhandler_linux.h"
#elif defined(DART_HOST_OS_MACOS)
#include "bin/eventhandler_macos.h"

View file

@ -1,443 +0,0 @@
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h"
#if defined(DART_HOST_OS_ANDROID)
#include "bin/eventhandler.h"
#include "bin/eventhandler_android.h"
#include <errno.h> // NOLINT
#include <fcntl.h> // NOLINT
#include <pthread.h> // NOLINT
#include <stdio.h> // NOLINT
#include <string.h> // NOLINT
#include <sys/epoll.h> // NOLINT
#include <sys/stat.h> // NOLINT
#include <unistd.h> // NOLINT
#include "bin/dartutils.h"
#include "bin/fdutils.h"
#include "bin/lockers.h"
#include "bin/process.h"
#include "bin/socket.h"
#include "bin/thread.h"
#include "bin/utils.h"
#include "platform/hashmap.h"
#include "platform/syslog.h"
#include "platform/utils.h"
// Android doesn't define EPOLLRDHUP.
#if !defined(EPOLLRDHUP)
#define EPOLLRDHUP 0x2000
#endif // !defined(EPOLLRDHUP)
namespace dart {
namespace bin {
intptr_t DescriptorInfo::GetPollEvents() {
// Do not ask for EPOLLERR and EPOLLHUP explicitly as they are
// triggered anyway.
intptr_t events = 0;
if ((Mask() & (1 << kInEvent)) != 0) {
events |= EPOLLIN;
}
if ((Mask() & (1 << kOutEvent)) != 0) {
events |= EPOLLOUT;
}
return events;
}
// Unregister the file descriptor for a DescriptorInfo structure with
// epoll.
static void RemoveFromEpollInstance(intptr_t epoll_fd_, DescriptorInfo* di) {
VOID_NO_RETRY_EXPECTED(
epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, di->fd(), nullptr));
}
static void AddToEpollInstance(intptr_t epoll_fd_, DescriptorInfo* di) {
struct epoll_event event;
event.events = EPOLLRDHUP | di->GetPollEvents();
if (!di->IsListeningSocket()) {
event.events |= EPOLLET;
}
event.data.ptr = di;
int status =
NO_RETRY_EXPECTED(epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, di->fd(), &event));
if (status == -1) {
// TODO(dart:io): Verify that the dart end is handling this correctly.
// Epoll does not accept the file descriptor. It could be due to
// already closed file descriptor, or unsupported devices, such
// as /dev/null. In such case, mark the file descriptor as closed,
// so dart will handle it accordingly.
di->NotifyAllDartPorts(1 << kCloseEvent);
}
}
EventHandlerImplementation::EventHandlerImplementation()
: socket_map_(&SimpleHashMap::SamePointerValue, 16) {
intptr_t result;
result = NO_RETRY_EXPECTED(pipe(interrupt_fds_));
if (result != 0) {
FATAL("Pipe creation failed");
}
if (!FDUtils::SetNonBlocking(interrupt_fds_[0])) {
FATAL("Failed to set pipe fd non blocking\n");
}
if (!FDUtils::SetCloseOnExec(interrupt_fds_[0])) {
FATAL("Failed to set pipe fd close on exec\n");
}
if (!FDUtils::SetCloseOnExec(interrupt_fds_[1])) {
FATAL("Failed to set pipe fd close on exec\n");
}
shutdown_ = false;
// The initial size passed to epoll_create is ignored on newer (>= 2.6.8)
// Linux versions
const int kEpollInitialSize = 64;
epoll_fd_ = NO_RETRY_EXPECTED(epoll_create(kEpollInitialSize));
if (epoll_fd_ == -1) {
FATAL("Failed creating epoll file descriptor: %i", errno);
}
if (!FDUtils::SetCloseOnExec(epoll_fd_)) {
FATAL("Failed to set epoll fd close on exec\n");
}
// Register the interrupt_fd with the epoll instance.
struct epoll_event event;
event.events = EPOLLIN;
event.data.ptr = nullptr;
int status = NO_RETRY_EXPECTED(
epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, interrupt_fds_[0], &event));
if (status == -1) {
FATAL("Failed adding interrupt fd to epoll instance");
}
}
static void DeleteDescriptorInfo(void* info) {
DescriptorInfo* di = reinterpret_cast<DescriptorInfo*>(info);
di->Close();
delete di;
}
EventHandlerImplementation::~EventHandlerImplementation() {
socket_map_.Clear(DeleteDescriptorInfo);
close(epoll_fd_);
close(interrupt_fds_[0]);
close(interrupt_fds_[1]);
}
void EventHandlerImplementation::UpdateEpollInstance(intptr_t old_mask,
DescriptorInfo* di) {
intptr_t new_mask = di->Mask();
if ((old_mask != 0) && (new_mask == 0)) {
RemoveFromEpollInstance(epoll_fd_, di);
} else if ((old_mask == 0) && (new_mask != 0)) {
AddToEpollInstance(epoll_fd_, di);
} else if ((old_mask != 0) && (new_mask != 0) && (old_mask != new_mask)) {
ASSERT(!di->IsListeningSocket());
RemoveFromEpollInstance(epoll_fd_, di);
AddToEpollInstance(epoll_fd_, di);
}
}
DescriptorInfo* EventHandlerImplementation::GetDescriptorInfo(
intptr_t fd,
bool is_listening) {
ASSERT(fd >= 0);
SimpleHashMap::Entry* entry = socket_map_.Lookup(
GetHashmapKeyFromFd(fd), GetHashmapHashFromFd(fd), true);
ASSERT(entry != nullptr);
DescriptorInfo* di = reinterpret_cast<DescriptorInfo*>(entry->value);
if (di == nullptr) {
// If there is no data in the hash map for this file descriptor a
// new DescriptorInfo for the file descriptor is inserted.
if (is_listening) {
di = new DescriptorInfoMultiple(fd);
} else {
di = new DescriptorInfoSingle(fd);
}
entry->value = di;
}
ASSERT(fd == di->fd());
return di;
}
void EventHandlerImplementation::WakeupHandler(intptr_t id,
Dart_Port dart_port,
int64_t data) {
InterruptMessage msg;
msg.id = id;
msg.dart_port = dart_port;
msg.data = data;
// WriteToBlocking will write up to 512 bytes atomically, and since our msg
// is smaller than 512, we don't need a thread lock.
// See: http://linux.die.net/man/7/pipe, section 'Pipe_buf'.
ASSERT(kInterruptMessageSize < PIPE_BUF);
intptr_t result =
FDUtils::WriteToBlocking(interrupt_fds_[1], &msg, kInterruptMessageSize);
if (result != kInterruptMessageSize) {
if (result == -1) {
FATAL("Interrupt message failure: %s", strerror(errno));
} else {
FATAL("Interrupt message failure: expected to write %" Pd
" bytes, but wrote %" Pd ".",
kInterruptMessageSize, result);
}
}
}
void EventHandlerImplementation::HandleInterruptFd() {
const intptr_t MAX_MESSAGES = kInterruptMessageSize;
InterruptMessage msg[MAX_MESSAGES];
ssize_t bytes = TEMP_FAILURE_RETRY_NO_SIGNAL_BLOCKER(
read(interrupt_fds_[0], msg, MAX_MESSAGES * kInterruptMessageSize));
for (ssize_t i = 0; i < bytes / kInterruptMessageSize; i++) {
if (msg[i].id == kTimerId) {
timeout_queue_.UpdateTimeout(msg[i].dart_port, msg[i].data);
} else if (msg[i].id == kShutdownId) {
shutdown_ = true;
} else {
ASSERT((msg[i].data & COMMAND_MASK) != 0);
Socket* socket = reinterpret_cast<Socket*>(msg[i].id);
RefCntReleaseScope<Socket> rs(socket);
if (socket->fd() == -1) {
continue;
}
DescriptorInfo* di =
GetDescriptorInfo(socket->fd(), IS_LISTENING_SOCKET(msg[i].data));
if (IS_COMMAND(msg[i].data, kShutdownReadCommand)) {
ASSERT(!di->IsListeningSocket());
// Close the socket for reading.
VOID_NO_RETRY_EXPECTED(shutdown(di->fd(), SHUT_RD));
} else if (IS_COMMAND(msg[i].data, kShutdownWriteCommand)) {
ASSERT(!di->IsListeningSocket());
// Close the socket for writing.
VOID_NO_RETRY_EXPECTED(shutdown(di->fd(), SHUT_WR));
} else if (IS_COMMAND(msg[i].data, kCloseCommand)) {
// Close the socket and free system resources and move on to next
// message.
if (IS_SIGNAL_SOCKET(msg[i].data)) {
Process::ClearSignalHandlerByFd(di->fd(), socket->isolate_port());
}
intptr_t old_mask = di->Mask();
Dart_Port port = msg[i].dart_port;
if (port != ILLEGAL_PORT) {
di->RemovePort(port);
}
intptr_t new_mask = di->Mask();
UpdateEpollInstance(old_mask, di);
intptr_t fd = di->fd();
if (di->IsListeningSocket()) {
// We only close the socket file descriptor from the operating
// system if there are no other dart socket objects which
// are listening on the same (address, port) combination.
ListeningSocketRegistry* registry =
ListeningSocketRegistry::Instance();
MutexLocker locker(registry->mutex());
if (registry->CloseSafe(socket)) {
ASSERT(new_mask == 0);
socket_map_.Remove(GetHashmapKeyFromFd(fd),
GetHashmapHashFromFd(fd));
di->Close();
delete di;
}
socket->CloseFd();
} else {
ASSERT(new_mask == 0);
socket_map_.Remove(GetHashmapKeyFromFd(fd), GetHashmapHashFromFd(fd));
di->Close();
delete di;
socket->CloseFd();
}
DartUtils::PostInt32(port, 1 << kDestroyedEvent);
} else if (IS_COMMAND(msg[i].data, kReturnTokenCommand)) {
int count = TOKEN_COUNT(msg[i].data);
intptr_t old_mask = di->Mask();
di->ReturnTokens(msg[i].dart_port, count);
UpdateEpollInstance(old_mask, di);
} else if (IS_COMMAND(msg[i].data, kSetEventMaskCommand)) {
// `events` can only have kInEvent/kOutEvent flags set.
intptr_t events = msg[i].data & EVENT_MASK;
ASSERT(0 == (events & ~(1 << kInEvent | 1 << kOutEvent)));
intptr_t old_mask = di->Mask();
di->SetPortAndMask(msg[i].dart_port, msg[i].data & EVENT_MASK);
UpdateEpollInstance(old_mask, di);
} else {
UNREACHABLE();
}
}
}
}
#ifdef DEBUG_POLL
static void PrintEventMask(intptr_t fd, intptr_t events) {
Syslog::Print("%d ", fd);
if ((events & EPOLLIN) != 0) {
Syslog::Print("EPOLLIN ");
}
if ((events & EPOLLPRI) != 0) {
Syslog::Print("EPOLLPRI ");
}
if ((events & EPOLLOUT) != 0) {
Syslog::Print("EPOLLOUT ");
}
if ((events & EPOLLERR) != 0) {
Syslog::Print("EPOLLERR ");
}
if ((events & EPOLLHUP) != 0) {
Syslog::Print("EPOLLHUP ");
}
if ((events & EPOLLRDHUP) != 0) {
Syslog::Print("EPOLLRDHUP ");
}
int all_events =
EPOLLIN | EPOLLPRI | EPOLLOUT | EPOLLERR | EPOLLHUP | EPOLLRDHUP;
if ((events & ~all_events) != 0) {
Syslog::Print("(and %08x) ", events & ~all_events);
}
Syslog::Print("(available %d) ", FDUtils::AvailableBytes(fd));
Syslog::Print("\n");
}
#endif
intptr_t EventHandlerImplementation::GetPollEvents(intptr_t events,
DescriptorInfo* di) {
#ifdef DEBUG_POLL
PrintEventMask(di->fd(), events);
#endif
if ((events & EPOLLERR) != 0) {
// Return error only if EPOLLIN is present.
return ((events & EPOLLIN) != 0) ? (1 << kErrorEvent) : 0;
}
intptr_t event_mask = 0;
if ((events & EPOLLIN) != 0) {
event_mask |= (1 << kInEvent);
}
if ((events & EPOLLOUT) != 0) {
event_mask |= (1 << kOutEvent);
}
if ((events & (EPOLLHUP | EPOLLRDHUP)) != 0) {
event_mask |= (1 << kCloseEvent);
}
return event_mask;
}
void EventHandlerImplementation::HandleEvents(struct epoll_event* events,
int size) {
bool interrupt_seen = false;
for (int i = 0; i < size; i++) {
if (events[i].data.ptr == nullptr) {
interrupt_seen = true;
} else {
DescriptorInfo* di =
reinterpret_cast<DescriptorInfo*>(events[i].data.ptr);
const intptr_t old_mask = di->Mask();
const intptr_t event_mask = GetPollEvents(events[i].events, di);
if ((event_mask & (1 << kErrorEvent)) != 0) {
di->NotifyAllDartPorts(event_mask);
UpdateEpollInstance(old_mask, di);
} else if (event_mask != 0) {
Dart_Port port = di->NextNotifyDartPort(event_mask);
ASSERT(port != 0);
UpdateEpollInstance(old_mask, di);
DartUtils::PostInt32(port, event_mask);
}
}
}
if (interrupt_seen) {
// Handle after socket events, so we avoid closing a socket before we handle
// the current events.
HandleInterruptFd();
}
}
int64_t EventHandlerImplementation::GetTimeout() {
if (!timeout_queue_.HasTimeout()) {
return kInfinityTimeout;
}
int64_t millis =
timeout_queue_.CurrentTimeout() - TimerUtils::GetCurrentMonotonicMillis();
return (millis < 0) ? 0 : millis;
}
void EventHandlerImplementation::HandleTimeout() {
if (timeout_queue_.HasTimeout()) {
int64_t millis = timeout_queue_.CurrentTimeout() -
TimerUtils::GetCurrentMonotonicMillis();
if (millis <= 0) {
DartUtils::PostNull(timeout_queue_.CurrentPort());
timeout_queue_.RemoveCurrent();
}
}
}
void EventHandlerImplementation::Poll(uword args) {
ThreadSignalBlocker signal_blocker(SIGPROF);
const intptr_t kMaxEvents = 16;
struct epoll_event events[kMaxEvents];
EventHandler* handler = reinterpret_cast<EventHandler*>(args);
EventHandlerImplementation* handler_impl = &handler->delegate_;
ASSERT(handler_impl != nullptr);
while (!handler_impl->shutdown_) {
int64_t millis = handler_impl->GetTimeout();
ASSERT((millis == kInfinityTimeout) || (millis >= 0));
if (millis > kMaxInt32) {
millis = kMaxInt32;
}
intptr_t result = TEMP_FAILURE_RETRY_NO_SIGNAL_BLOCKER(
epoll_wait(handler_impl->epoll_fd_, events, kMaxEvents, millis));
ASSERT(EAGAIN == EWOULDBLOCK);
if (result == -1) {
if (errno != EWOULDBLOCK) {
perror("Poll failed");
}
} else {
handler_impl->HandleTimeout();
handler_impl->HandleEvents(events, result);
}
}
DEBUG_ASSERT(ReferenceCounted<Socket>::instances() == 0);
handler->NotifyShutdownDone();
}
void EventHandlerImplementation::Start(EventHandler* handler) {
int result =
Thread::Start("dart:io EventHandler", &EventHandlerImplementation::Poll,
reinterpret_cast<uword>(handler));
if (result != 0) {
FATAL("Failed to start event handler thread %d", result);
}
}
void EventHandlerImplementation::Shutdown() {
SendData(kShutdownId, 0, 0);
}
void EventHandlerImplementation::SendData(intptr_t id,
Dart_Port dart_port,
int64_t data) {
WakeupHandler(id, dart_port, data);
}
void* EventHandlerImplementation::GetHashmapKeyFromFd(intptr_t fd) {
// The hashmap does not support keys with value 0.
return reinterpret_cast<void*>(fd + 1);
}
uint32_t EventHandlerImplementation::GetHashmapHashFromFd(intptr_t fd) {
// The hashmap does not support keys with value 0.
return dart::Utils::WordHash(fd + 1);
}
} // namespace bin
} // namespace dart
#endif // defined(DART_HOST_OS_ANDROID)

View file

@ -1,100 +0,0 @@
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#ifndef RUNTIME_BIN_EVENTHANDLER_ANDROID_H_
#define RUNTIME_BIN_EVENTHANDLER_ANDROID_H_
#if !defined(RUNTIME_BIN_EVENTHANDLER_H_)
#error Do not include eventhandler_android.h directly;
#error use eventhandler.h instead.
#endif
#include <errno.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <unistd.h>
#include "platform/hashmap.h"
#include "platform/signal_blocker.h"
namespace dart {
namespace bin {
class DescriptorInfo : public DescriptorInfoBase {
public:
explicit DescriptorInfo(intptr_t fd) : DescriptorInfoBase(fd) {}
virtual ~DescriptorInfo() {}
intptr_t GetPollEvents();
virtual void Close() {
close(fd_);
fd_ = -1;
}
private:
DISALLOW_COPY_AND_ASSIGN(DescriptorInfo);
};
class DescriptorInfoSingle : public DescriptorInfoSingleMixin<DescriptorInfo> {
public:
explicit DescriptorInfoSingle(intptr_t fd)
: DescriptorInfoSingleMixin(fd, false) {}
virtual ~DescriptorInfoSingle() {}
private:
DISALLOW_COPY_AND_ASSIGN(DescriptorInfoSingle);
};
class DescriptorInfoMultiple
: public DescriptorInfoMultipleMixin<DescriptorInfo> {
public:
explicit DescriptorInfoMultiple(intptr_t fd)
: DescriptorInfoMultipleMixin(fd, false) {}
virtual ~DescriptorInfoMultiple() {}
private:
DISALLOW_COPY_AND_ASSIGN(DescriptorInfoMultiple);
};
class EventHandlerImplementation {
public:
EventHandlerImplementation();
~EventHandlerImplementation();
void UpdateEpollInstance(intptr_t old_mask, DescriptorInfo* di);
// Gets the socket data structure for a given file
// descriptor. Creates a new one if one is not found.
DescriptorInfo* GetDescriptorInfo(intptr_t fd, bool is_listening);
void SendData(intptr_t id, Dart_Port dart_port, int64_t data);
void Start(EventHandler* handler);
void Shutdown();
private:
int64_t GetTimeout();
void HandleEvents(struct epoll_event* events, int size);
void HandleTimeout();
static void Poll(uword args);
void WakeupHandler(intptr_t id, Dart_Port dart_port, int64_t data);
void HandleInterruptFd();
void SetPort(intptr_t fd, Dart_Port dart_port, intptr_t mask);
intptr_t GetPollEvents(intptr_t events, DescriptorInfo* di);
static void* GetHashmapKeyFromFd(intptr_t fd);
static uint32_t GetHashmapHashFromFd(intptr_t fd);
SimpleHashMap socket_map_;
TimeoutQueue timeout_queue_;
bool shutdown_;
int interrupt_fds_[2];
int epoll_fd_;
DISALLOW_COPY_AND_ASSIGN(EventHandlerImplementation);
};
} // namespace bin
} // namespace dart
#endif // RUNTIME_BIN_EVENTHANDLER_ANDROID_H_

View file

@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h"
#if defined(DART_HOST_OS_LINUX)
#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
#include "bin/eventhandler.h"
#include "bin/eventhandler_linux.h"
@ -444,4 +444,4 @@ uint32_t EventHandlerImplementation::GetHashmapHashFromFd(intptr_t fd) {
} // namespace bin
} // namespace dart
#endif // defined(DART_HOST_OS_LINUX)
#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)

View file

@ -1,142 +0,0 @@
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h"
#if defined(DART_HOST_OS_ANDROID)
#include "bin/fdutils.h"
#include <errno.h> // NOLINT
#include <fcntl.h> // NOLINT
#include <sys/ioctl.h> // NOLINT
#include <unistd.h> // NOLINT
#include "platform/signal_blocker.h"
namespace dart {
namespace bin {
bool FDUtils::SetCloseOnExec(intptr_t fd) {
intptr_t status;
status = NO_RETRY_EXPECTED(fcntl(fd, F_GETFD));
if (status < 0) {
perror("fcntl(F_GETFD) failed");
return false;
}
status |= FD_CLOEXEC;
if (NO_RETRY_EXPECTED(fcntl(fd, F_SETFD, status)) < 0) {
perror("fcntl(F_SETFD, FD_CLOEXEC) failed");
return false;
}
return true;
}
static bool SetBlockingHelper(intptr_t fd, bool blocking) {
intptr_t status;
status = NO_RETRY_EXPECTED(fcntl(fd, F_GETFL));
if (status < 0) {
perror("fcntl(F_GETFL) failed");
return false;
}
status = blocking ? (status & ~O_NONBLOCK) : (status | O_NONBLOCK);
if (NO_RETRY_EXPECTED(fcntl(fd, F_SETFL, status)) < 0) {
perror("fcntl(F_SETFL, O_NONBLOCK) failed");
return false;
}
return true;
}
bool FDUtils::SetNonBlocking(intptr_t fd) {
return SetBlockingHelper(fd, false);
}
bool FDUtils::SetBlocking(intptr_t fd) {
return SetBlockingHelper(fd, true);
}
bool FDUtils::IsBlocking(intptr_t fd, bool* is_blocking) {
intptr_t status;
status = NO_RETRY_EXPECTED(fcntl(fd, F_GETFL));
if (status < 0) {
return false;
}
*is_blocking = (status & O_NONBLOCK) == 0;
return true;
}
intptr_t FDUtils::AvailableBytes(intptr_t fd) {
int available; // ioctl for FIONREAD expects an 'int*' argument.
int result = NO_RETRY_EXPECTED(ioctl(fd, FIONREAD, &available));
if (result < 0) {
return result;
}
ASSERT(available >= 0);
return static_cast<intptr_t>(available);
}
ssize_t FDUtils::ReadFromBlocking(int fd, void* buffer, size_t count) {
#ifdef DEBUG
bool is_blocking = false;
ASSERT(FDUtils::IsBlocking(fd, &is_blocking));
ASSERT(is_blocking);
#endif
size_t remaining = count;
char* buffer_pos = reinterpret_cast<char*>(buffer);
while (remaining > 0) {
ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer_pos, remaining));
if (bytes_read == 0) {
return count - remaining;
} else if (bytes_read == -1) {
ASSERT(EAGAIN == EWOULDBLOCK);
// Error code EWOULDBLOCK should only happen for non blocking
// file descriptors.
ASSERT(errno != EWOULDBLOCK);
return -1;
} else {
ASSERT(bytes_read > 0);
remaining -= bytes_read;
buffer_pos += bytes_read;
}
}
return count;
}
ssize_t FDUtils::WriteToBlocking(int fd, const void* buffer, size_t count) {
#ifdef DEBUG
bool is_blocking = false;
ASSERT(FDUtils::IsBlocking(fd, &is_blocking));
ASSERT(is_blocking);
#endif
size_t remaining = count;
char* buffer_pos = const_cast<char*>(reinterpret_cast<const char*>(buffer));
while (remaining > 0) {
ssize_t bytes_written =
TEMP_FAILURE_RETRY(write(fd, buffer_pos, remaining));
if (bytes_written == 0) {
return count - remaining;
} else if (bytes_written == -1) {
ASSERT(EAGAIN == EWOULDBLOCK);
// Error code EWOULDBLOCK should only happen for non blocking
// file descriptors.
ASSERT(errno != EWOULDBLOCK);
return -1;
} else {
ASSERT(bytes_written > 0);
remaining -= bytes_written;
buffer_pos += bytes_written;
}
}
return count;
}
void FDUtils::SaveErrorAndClose(intptr_t fd) {
int err = errno;
close(fd);
errno = err;
}
} // namespace bin
} // namespace dart
#endif // defined(DART_HOST_OS_ANDROID)

View file

@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h"
#if defined(DART_HOST_OS_LINUX)
#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
#include "bin/fdutils.h"
@ -139,4 +139,4 @@ void FDUtils::SaveErrorAndClose(intptr_t fd) {
} // namespace bin
} // namespace dart
#endif // defined(DART_HOST_OS_LINUX)
#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)

View file

@ -1,793 +0,0 @@
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h"
#if defined(DART_HOST_OS_ANDROID)
#include "bin/file.h"
#include <errno.h> // NOLINT
#include <fcntl.h> // NOLINT
#include <libgen.h> // NOLINT
#include <sys/mman.h> // NOLINT
#include <sys/sendfile.h> // NOLINT
#include <sys/stat.h> // NOLINT
#include <sys/syscall.h> // NOLINT
#include <sys/types.h> // NOLINT
#include <unistd.h> // NOLINT
#include <utime.h> // NOLINT
#include "bin/builtin.h"
#include "bin/fdutils.h"
#include "bin/namespace.h"
#include "platform/signal_blocker.h"
#include "platform/syslog.h"
#include "platform/utils.h"
namespace dart {
namespace bin {
class FileHandle {
public:
explicit FileHandle(int fd) : fd_(fd) {}
~FileHandle() {}
int fd() const { return fd_; }
void set_fd(int fd) { fd_ = fd; }
private:
int fd_;
DISALLOW_COPY_AND_ASSIGN(FileHandle);
};
File::~File() {
if (!IsClosed() && (handle_->fd() != STDOUT_FILENO) &&
(handle_->fd() != STDERR_FILENO)) {
Close();
}
delete handle_;
}
void File::Close() {
ASSERT(handle_->fd() >= 0);
if (handle_->fd() == STDOUT_FILENO) {
// If stdout, redirect fd to /dev/null.
int null_fd = TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY));
ASSERT(null_fd >= 0);
VOID_TEMP_FAILURE_RETRY(dup2(null_fd, handle_->fd()));
close(null_fd);
} else {
int err = close(handle_->fd());
if (err != 0) {
const int kBufferSize = 1024;
char error_buf[kBufferSize];
Syslog::PrintErr("%s\n", Utils::StrError(errno, error_buf, kBufferSize));
}
}
handle_->set_fd(kClosedFd);
}
intptr_t File::GetFD() {
return handle_->fd();
}
bool File::IsClosed() {
return handle_->fd() == kClosedFd;
}
MappedMemory* File::Map(MapType type,
int64_t position,
int64_t length,
void* start) {
ASSERT(handle_->fd() >= 0);
ASSERT(length > 0);
void* hint = nullptr;
int prot = PROT_NONE;
int flags = MAP_PRIVATE;
switch (type) {
case kReadOnly:
prot = PROT_READ;
break;
case kReadExecute:
// Try to allocate near the VM's binary.
hint = reinterpret_cast<void*>(&Dart_Initialize);
prot = PROT_READ | PROT_EXEC;
break;
case kReadWrite:
prot = PROT_READ | PROT_WRITE;
break;
}
if (start != nullptr) {
hint = start;
flags |= MAP_FIXED;
}
void* addr = mmap(hint, length, prot, flags, handle_->fd(), position);
if (addr == MAP_FAILED) {
return nullptr;
}
return new MappedMemory(addr, length, /*should_unmap=*/start == nullptr);
}
void MappedMemory::Unmap() {
int result = munmap(address_, size_);
ASSERT(result == 0);
address_ = nullptr;
size_ = 0;
}
int64_t File::Read(void* buffer, int64_t num_bytes) {
ASSERT(handle_->fd() >= 0);
return TEMP_FAILURE_RETRY(read(handle_->fd(), buffer, num_bytes));
}
int64_t File::Write(const void* buffer, int64_t num_bytes) {
ASSERT(handle_->fd() >= 0);
return TEMP_FAILURE_RETRY(write(handle_->fd(), buffer, num_bytes));
}
bool File::VPrint(const char* format, va_list args) {
// Measure.
va_list measure_args;
va_copy(measure_args, args);
intptr_t len = vsnprintf(nullptr, 0, format, measure_args);
va_end(measure_args);
char* buffer = reinterpret_cast<char*>(malloc(len + 1));
// Print.
va_list print_args;
va_copy(print_args, args);
vsnprintf(buffer, len + 1, format, print_args);
va_end(print_args);
bool result = WriteFully(buffer, len);
free(buffer);
return result;
}
int64_t File::Position() {
ASSERT(handle_->fd() >= 0);
return NO_RETRY_EXPECTED(lseek64(handle_->fd(), 0, SEEK_CUR));
}
bool File::SetPosition(int64_t position) {
ASSERT(handle_->fd() >= 0);
return NO_RETRY_EXPECTED(lseek64(handle_->fd(), position, SEEK_SET)) >= 0;
}
bool File::Truncate(int64_t length) {
ASSERT(handle_->fd() >= 0);
return TEMP_FAILURE_RETRY(ftruncate(handle_->fd(), length) != -1);
}
bool File::Flush() {
ASSERT(handle_->fd() >= 0);
return NO_RETRY_EXPECTED(fsync(handle_->fd()) != -1);
}
bool File::Lock(File::LockType lock, int64_t start, int64_t end) {
ASSERT(handle_->fd() >= 0);
ASSERT((end == -1) || (end > start));
struct flock fl;
switch (lock) {
case File::kLockUnlock:
fl.l_type = F_UNLCK;
break;
case File::kLockShared:
case File::kLockBlockingShared:
fl.l_type = F_RDLCK;
break;
case File::kLockExclusive:
case File::kLockBlockingExclusive:
fl.l_type = F_WRLCK;
break;
default:
return false;
}
fl.l_whence = SEEK_SET;
fl.l_start = start;
fl.l_len = end == -1 ? 0 : end - start;
int cmd = F_SETLK;
if ((lock == File::kLockBlockingShared) ||
(lock == File::kLockBlockingExclusive)) {
cmd = F_SETLKW;
}
return TEMP_FAILURE_RETRY(fcntl(handle_->fd(), cmd, &fl)) != -1;
}
int64_t File::Length() {
ASSERT(handle_->fd() >= 0);
struct stat st;
if (NO_RETRY_EXPECTED(fstat(handle_->fd(), &st)) == 0) {
return st.st_size;
}
return -1;
}
File* File::FileOpenW(const wchar_t* system_name, FileOpenMode mode) {
UNREACHABLE();
return nullptr;
}
File* File::OpenFD(int fd) {
return new File(new FileHandle(fd));
}
File* File::Open(Namespace* namespc, const char* name, FileOpenMode mode) {
NamespaceScope ns(namespc, name);
// Report errors for non-regular files.
struct stat st;
if (TEMP_FAILURE_RETRY(fstatat(ns.fd(), ns.path(), &st, 0)) == 0) {
// Only accept regular files, character devices, and pipes.
if (!S_ISREG(st.st_mode) && !S_ISCHR(st.st_mode) && !S_ISFIFO(st.st_mode)) {
errno = (S_ISDIR(st.st_mode)) ? EISDIR : ENOENT;
return nullptr;
}
}
int flags = O_RDONLY;
if ((mode & kWrite) != 0) {
ASSERT((mode & kWriteOnly) == 0);
flags = (O_RDWR | O_CREAT);
}
if ((mode & kWriteOnly) != 0) {
ASSERT((mode & kWrite) == 0);
flags = (O_WRONLY | O_CREAT);
}
if ((mode & kTruncate) != 0) {
flags = flags | O_TRUNC;
}
flags |= O_CLOEXEC;
const int fd = TEMP_FAILURE_RETRY(openat(ns.fd(), ns.path(), flags, 0666));
if (fd < 0) {
return nullptr;
}
if ((((mode & kWrite) != 0) && ((mode & kTruncate) == 0)) ||
(((mode & kWriteOnly) != 0) && ((mode & kTruncate) == 0))) {
int64_t position = NO_RETRY_EXPECTED(lseek(fd, 0, SEEK_END));
if (position < 0) {
return nullptr;
}
}
return new File(new FileHandle(fd));
}
Utils::CStringUniquePtr File::UriToPath(const char* uri) {
const char* path = (strlen(uri) >= 8 && strncmp(uri, "file:///", 8) == 0)
? uri + 7 : uri;
UriDecoder uri_decoder(path);
if (uri_decoder.decoded() == nullptr) {
errno = EINVAL;
return Utils::CreateCStringUniquePtr(nullptr);
}
return Utils::CreateCStringUniquePtr(strdup(uri_decoder.decoded()));
}
File* File::OpenUri(Namespace* namespc, const char* uri, FileOpenMode mode) {
auto path = UriToPath(uri);
if (path == nullptr) {
return nullptr;
}
return File::Open(namespc, path.get(), mode);
}
File* File::OpenStdio(int fd) {
return new File(new FileHandle(fd));
}
bool File::Exists(Namespace* namespc, const char* name) {
NamespaceScope ns(namespc, name);
struct stat st;
if (TEMP_FAILURE_RETRY(fstatat(ns.fd(), ns.path(), &st, 0)) == 0) {
// Everything but a directory and a link is a file to Dart.
return !S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode);
} else {
return false;
}
}
bool File::ExistsUri(Namespace* namespc, const char* uri) {
auto path = UriToPath(uri);
if (path == nullptr) {
return false;
}
return File::Exists(namespc, path.get());
}
bool File::Create(Namespace* namespc, const char* name, bool exclusive) {
NamespaceScope ns(namespc, name);
int flags = O_RDONLY | O_CREAT | O_CLOEXEC;
if (exclusive) {
flags |= O_EXCL;
}
const int fd = TEMP_FAILURE_RETRY(openat(ns.fd(), ns.path(), flags, 0666));
if (fd < 0) {
return false;
}
// File.create returns a File, so we shouldn't be giving the illusion that the
// call has created a file or that a file already exists if there is already
// an entity at the same path that is a directory or a link.
bool is_file = true;
struct stat st;
if (TEMP_FAILURE_RETRY(fstat(fd, &st)) == 0) {
if (S_ISDIR(st.st_mode)) {
errno = EISDIR;
is_file = false;
} else if (S_ISLNK(st.st_mode)) {
errno = ENOENT;
is_file = false;
}
}
FDUtils::SaveErrorAndClose(fd);
return is_file;
}
// symlinkat is added to Android libc in android-21.
static int SymlinkAt(const char* oldpath, int newdirfd, const char* newpath) {
return syscall(__NR_symlinkat, oldpath, newdirfd, newpath);
}
bool File::CreateLink(Namespace* namespc,
const char* name,
const char* target) {
NamespaceScope ns(namespc, name);
return NO_RETRY_EXPECTED(SymlinkAt(target, ns.fd(), ns.path())) == 0;
}
bool File::CreatePipe(Namespace* namespc, File** readPipe, File** writePipe) {
int pipe_fds[2];
int status = NO_RETRY_EXPECTED(pipe(pipe_fds));
if (status != 0) {
return false;
}
*readPipe = OpenFD(pipe_fds[0]);
*writePipe = OpenFD(pipe_fds[1]);
return true;
}
File::Type File::GetType(Namespace* namespc,
const char* name,
bool follow_links) {
NamespaceScope ns(namespc, name);
struct stat entry_info;
int stat_success;
if (follow_links) {
stat_success =
TEMP_FAILURE_RETRY(fstatat(ns.fd(), ns.path(), &entry_info, 0));
} else {
stat_success = TEMP_FAILURE_RETRY(
fstatat(ns.fd(), ns.path(), &entry_info, AT_SYMLINK_NOFOLLOW));
}
if (stat_success == -1) {
return File::kDoesNotExist;
}
if (S_ISDIR(entry_info.st_mode)) {
return File::kIsDirectory;
}
if (S_ISREG(entry_info.st_mode)) {
return File::kIsFile;
}
if (S_ISLNK(entry_info.st_mode)) {
return File::kIsLink;
}
if (S_ISSOCK(entry_info.st_mode)) {
return File::kIsSock;
}
if (S_ISFIFO(entry_info.st_mode)) {
return File::kIsPipe;
}
return File::kDoesNotExist;
}
static void SetErrno(File::Type type) {
switch (type) {
case File::kIsDirectory:
errno = EISDIR;
break;
case File::kDoesNotExist:
errno = ENOENT;
break;
default:
errno = EINVAL;
break;
}
}
static bool CheckTypeAndSetErrno(Namespace* namespc,
const char* name,
File::Type expected,
bool follow_links) {
File::Type actual = File::GetType(namespc, name, follow_links);
if (actual == expected) {
return true;
}
SetErrno(actual);
return false;
}
bool File::Delete(Namespace* namespc, const char* name) {
NamespaceScope ns(namespc, name);
File::Type type = File::GetType(namespc, name, true);
if (type == kIsFile || type == kIsSock || type == kIsPipe) {
return (NO_RETRY_EXPECTED(unlinkat(ns.fd(), ns.path(), 0)) == 0);
}
SetErrno(type);
return false;
}
bool File::DeleteLink(Namespace* namespc, const char* name) {
NamespaceScope ns(namespc, name);
return CheckTypeAndSetErrno(namespc, name, kIsLink, false) &&
(NO_RETRY_EXPECTED(unlinkat(ns.fd(), ns.path(), 0)) == 0);
}
bool File::Rename(Namespace* namespc,
const char* old_path,
const char* new_path) {
File::Type type = File::GetType(namespc, old_path, true);
if (type == kIsFile || type == kIsSock || type == kIsPipe) {
NamespaceScope oldns(namespc, old_path);
NamespaceScope newns(namespc, new_path);
return (NO_RETRY_EXPECTED(renameat(oldns.fd(), oldns.path(), newns.fd(),
newns.path())) == 0);
}
SetErrno(type);
return false;
}
bool File::RenameLink(Namespace* namespc,
const char* old_path,
const char* new_path) {
NamespaceScope oldns(namespc, old_path);
NamespaceScope newns(namespc, new_path);
return CheckTypeAndSetErrno(namespc, old_path, kIsLink, false) &&
(NO_RETRY_EXPECTED(renameat(oldns.fd(), oldns.path(), newns.fd(),
newns.path())) == 0);
}
bool File::Copy(Namespace* namespc,
const char* old_path,
const char* new_path) {
File::Type type = File::GetType(namespc, old_path, true);
if (type != kIsFile && type != kIsSock && type != kIsPipe) {
SetErrno(type);
return false;
}
NamespaceScope oldns(namespc, old_path);
struct stat st;
if (TEMP_FAILURE_RETRY(fstatat(oldns.fd(), oldns.path(), &st, 0)) != 0) {
return false;
}
const int old_fd = TEMP_FAILURE_RETRY(
openat(oldns.fd(), oldns.path(), O_RDONLY | O_CLOEXEC));
if (old_fd < 0) {
return false;
}
NamespaceScope newns(namespc, new_path);
const int new_fd = TEMP_FAILURE_RETRY(
openat(newns.fd(), newns.path(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC,
st.st_mode));
if (new_fd < 0) {
close(old_fd);
return false;
}
off_t offset = 0;
intptr_t result = 1;
while (result > 0) {
// Loop to ensure we copy everything, and not only up to 2GB.
result = NO_RETRY_EXPECTED(sendfile(new_fd, old_fd, &offset, kMaxUint32));
}
// From sendfile man pages:
// Applications may wish to fall back to read(2)/write(2) in the case
// where sendfile() fails with EINVAL or ENOSYS.
if ((result < 0) && ((errno == EINVAL) || (errno == ENOSYS))) {
const intptr_t kBufferSize = 8 * KB;
uint8_t* buffer = reinterpret_cast<uint8_t*>(malloc(kBufferSize));
while ((result = TEMP_FAILURE_RETRY(read(old_fd, buffer, kBufferSize))) >
0) {
int wrote = TEMP_FAILURE_RETRY(write(new_fd, buffer, result));
if (wrote != result) {
result = -1;
break;
}
}
free(buffer);
}
int e = errno;
close(old_fd);
close(new_fd);
if (result < 0) {
VOID_NO_RETRY_EXPECTED(unlinkat(newns.fd(), newns.path(), 0));
errno = e;
return false;
}
return true;
}
static bool StatHelper(Namespace* namespc, const char* name, struct stat* st) {
NamespaceScope ns(namespc, name);
if (TEMP_FAILURE_RETRY(fstatat(ns.fd(), ns.path(), st, 0)) != 0) {
return false;
}
// Signal an error if it's a directory.
if (S_ISDIR(st->st_mode)) {
errno = EISDIR;
return false;
}
// Otherwise assume the caller knows what it's doing.
return true;
}
int64_t File::LengthFromPath(Namespace* namespc, const char* name) {
struct stat st;
if (!StatHelper(namespc, name, &st)) {
return -1;
}
return st.st_size;
}
static void MillisecondsToTimespec(int64_t millis, struct timespec* t) {
ASSERT(t != nullptr);
t->tv_sec = millis / kMillisecondsPerSecond;
t->tv_nsec = (millis % kMillisecondsPerSecond) * 1000L;
}
void File::Stat(Namespace* namespc, const char* name, int64_t* data) {
NamespaceScope ns(namespc, name);
struct stat st;
if (TEMP_FAILURE_RETRY(fstatat(ns.fd(), ns.path(), &st, 0)) == 0) {
if (S_ISREG(st.st_mode)) {
data[kType] = kIsFile;
} else if (S_ISDIR(st.st_mode)) {
data[kType] = kIsDirectory;
} else if (S_ISLNK(st.st_mode)) {
data[kType] = kIsLink;
} else if (S_ISSOCK(st.st_mode)) {
data[kType] = kIsSock;
} else if (S_ISFIFO(st.st_mode)) {
data[kType] = kIsPipe;
} else {
data[kType] = kDoesNotExist;
}
data[kCreatedTime] = static_cast<int64_t>(st.st_ctime) * 1000;
data[kModifiedTime] = static_cast<int64_t>(st.st_mtime) * 1000;
data[kAccessedTime] = static_cast<int64_t>(st.st_atime) * 1000;
data[kMode] = st.st_mode;
data[kSize] = st.st_size;
} else {
data[kType] = kDoesNotExist;
}
}
time_t File::LastModified(Namespace* namespc, const char* name) {
struct stat st;
if (!StatHelper(namespc, name, &st)) {
return -1;
}
return st.st_mtime;
}
time_t File::LastAccessed(Namespace* namespc, const char* name) {
struct stat st;
if (!StatHelper(namespc, name, &st)) {
return -1;
}
return st.st_atime;
}
bool File::SetLastAccessed(Namespace* namespc,
const char* name,
int64_t millis) {
// First get the current times.
struct stat st;
if (!StatHelper(namespc, name, &st)) {
return false;
}
// Set the new time:
NamespaceScope ns(namespc, name);
struct timespec times[2];
MillisecondsToTimespec(millis, &times[0]);
MillisecondsToTimespec(static_cast<int64_t>(st.st_mtime) * 1000, &times[1]);
return utimensat(ns.fd(), ns.path(), times, 0) == 0;
}
bool File::SetLastModified(Namespace* namespc,
const char* name,
int64_t millis) {
// First get the current times.
struct stat st;
if (!StatHelper(namespc, name, &st)) {
return false;
}
// Set the new time:
NamespaceScope ns(namespc, name);
struct timespec times[2];
MillisecondsToTimespec(static_cast<int64_t>(st.st_atime) * 1000, &times[0]);
MillisecondsToTimespec(millis, &times[1]);
return utimensat(ns.fd(), ns.path(), times, 0) == 0;
}
// readlinkat is added to Android libc in android-21.
static int ReadLinkAt(int dirfd,
const char* pathname,
char* buf,
size_t bufsize) {
return syscall(__NR_readlinkat, dirfd, pathname, buf, bufsize);
}
const char* File::LinkTarget(Namespace* namespc,
const char* name,
char* dest,
int dest_size) {
NamespaceScope ns(namespc, name);
struct stat link_stats;
const int status = TEMP_FAILURE_RETRY(
fstatat(ns.fd(), ns.path(), &link_stats, AT_SYMLINK_NOFOLLOW));
if (status != 0) {
return nullptr;
}
if (!S_ISLNK(link_stats.st_mode)) {
errno = ENOENT;
return nullptr;
}
// Don't rely on the link_stats.st_size for the size of the link
// target. For some filesystems, e.g. procfs, this value is always
// 0. Also the link might have changed before the readlink call.
const int kBufferSize = PATH_MAX + 1;
char target[kBufferSize];
const int target_size =
TEMP_FAILURE_RETRY(ReadLinkAt(ns.fd(), ns.path(), target, kBufferSize));
if (target_size <= 0) {
return nullptr;
}
if (dest == nullptr) {
dest = DartUtils::ScopedCString(target_size + 1);
} else {
ASSERT(dest_size > 0);
if (dest_size <= target_size) {
return nullptr;
}
}
memmove(dest, target, target_size);
dest[target_size] = '\0';
return dest;
}
bool File::IsAbsolutePath(const char* pathname) {
return ((pathname != nullptr) && (pathname[0] == '/'));
}
intptr_t File::ReadLinkInto(const char* pathname,
char* result,
size_t result_size) {
ASSERT(pathname != nullptr);
ASSERT(IsAbsolutePath(pathname));
struct stat link_stats;
if (TEMP_FAILURE_RETRY(lstat(pathname, &link_stats)) != 0) {
return -1;
}
if (!S_ISLNK(link_stats.st_mode)) {
errno = ENOENT;
return -1;
}
size_t target_size =
TEMP_FAILURE_RETRY(readlink(pathname, result, result_size));
if (target_size <= 0) {
return -1;
}
// readlink returns non-zero terminated strings. Append.
if (target_size < result_size) {
result[target_size] = '\0';
target_size++;
}
return target_size;
}
const char* File::ReadLink(const char* pathname) {
// Don't rely on the link_stats.st_size for the size of the link
// target. For some filesystems, e.g. procfs, this value is always
// 0. Also the link might have changed before the readlink call.
const int kBufferSize = PATH_MAX + 1;
char target[kBufferSize];
size_t target_size = ReadLinkInto(pathname, target, kBufferSize);
if (target_size <= 0) {
return nullptr;
}
char* target_name = DartUtils::ScopedCString(target_size);
ASSERT(target_name != nullptr);
memmove(target_name, target, target_size);
return target_name;
}
const char* File::GetCanonicalPath(Namespace* namespc,
const char* name,
char* dest,
int dest_size) {
if (name == nullptr) {
return nullptr;
}
if (!Namespace::IsDefault(namespc)) {
// TODO(zra): There is no realpathat(). Also chasing a symlink might result
// in a path to something outside of the namespace, so canonicalizing paths
// would have to be done carefully. For now, don't do anything.
return name;
}
char* abs_path;
if (dest == nullptr) {
dest = DartUtils::ScopedCString(PATH_MAX + 1);
} else {
ASSERT(dest_size >= PATH_MAX);
}
ASSERT(dest != nullptr);
do {
abs_path = realpath(name, dest);
} while ((abs_path == nullptr) && (errno == EINTR));
ASSERT(abs_path == nullptr || IsAbsolutePath(abs_path));
ASSERT(abs_path == nullptr || (abs_path == dest));
return abs_path;
}
const char* File::PathSeparator() {
return "/";
}
const char* File::StringEscapedPathSeparator() {
return "/";
}
File::StdioHandleType File::GetStdioHandleType(int fd) {
struct stat buf;
int result = fstat(fd, &buf);
if (result == -1) {
return kTypeError;
}
if (S_ISCHR(buf.st_mode)) {
return kTerminal;
}
if (S_ISFIFO(buf.st_mode)) {
return kPipe;
}
if (S_ISSOCK(buf.st_mode)) {
return kSocket;
}
if (S_ISREG(buf.st_mode)) {
return kFile;
}
return kOther;
}
File::Identical File::AreIdentical(Namespace* namespc_1,
const char* file_1,
Namespace* namespc_2,
const char* file_2) {
struct stat file_1_info;
struct stat file_2_info;
int status;
{
NamespaceScope ns1(namespc_1, file_1);
status = TEMP_FAILURE_RETRY(
fstatat(ns1.fd(), ns1.path(), &file_1_info, AT_SYMLINK_NOFOLLOW));
if (status == -1) {
return File::kError;
}
}
{
NamespaceScope ns2(namespc_2, file_2);
status = TEMP_FAILURE_RETRY(
fstatat(ns2.fd(), ns2.path(), &file_2_info, AT_SYMLINK_NOFOLLOW));
if (status == -1) {
return File::kError;
}
}
return ((file_1_info.st_ino == file_2_info.st_ino) &&
(file_1_info.st_dev == file_2_info.st_dev))
? File::kIdentical
: File::kDifferent;
}
} // namespace bin
} // namespace dart
#endif // defined(DART_HOST_OS_ANDROID)

View file

@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h"
#if defined(DART_HOST_OS_LINUX)
#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
#include "bin/file.h"
@ -103,6 +103,7 @@ MappedMemory* File::Map(MapType type,
}
void* addr = mmap(hint, length, prot, flags, handle_->fd(), position);
#if defined(DART_HOST_OS_LINUX)
// On WSL 1 trying to allocate memory close to the binary by supplying a hint
// fails with ENOMEM for unclear reason. Some reports suggest that this might
// be related to the alignment of the hint but aligning it by 64Kb does not
@ -112,6 +113,7 @@ MappedMemory* File::Map(MapType type,
Utils::IsWindowsSubsystemForLinux()) {
addr = mmap(nullptr, length, prot, flags, handle_->fd(), position);
}
#endif
if (addr == MAP_FAILED) {
return nullptr;
@ -795,4 +797,4 @@ File::Identical File::AreIdentical(Namespace* namespc_1,
} // namespace bin
} // namespace dart
#endif // defined(DART_HOST_OS_LINUX)
#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)

View file

@ -1,155 +0,0 @@
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h"
#if defined(DART_HOST_OS_ANDROID)
#include "bin/file_system_watcher.h"
#include <errno.h> // NOLINT
#include <sys/inotify.h> // NOLINT
#include "bin/fdutils.h"
#include "bin/file.h"
#include "bin/socket.h"
#include "platform/signal_blocker.h"
namespace dart {
namespace bin {
bool FileSystemWatcher::IsSupported() {
return true;
}
intptr_t FileSystemWatcher::Init() {
int id = NO_RETRY_EXPECTED(inotify_init());
if (id < 0 || !FDUtils::SetCloseOnExec(id)) {
return -1;
}
// Some systems don't support setting this as non-blocking. Since watching
// internals are kept away from the user, we know it's possible to continue,
// even if setting non-blocking fails.
FDUtils::SetNonBlocking(id);
return id;
}
void FileSystemWatcher::Close(intptr_t id) {
USE(id);
}
intptr_t FileSystemWatcher::WatchPath(intptr_t id,
Namespace* namespc,
const char* path,
int events,
bool recursive) {
int list_events = IN_DELETE_SELF | IN_MOVE_SELF;
if ((events & kCreate) != 0) {
list_events |= IN_CREATE;
}
if ((events & kModifyContent) != 0) {
list_events |= IN_CLOSE_WRITE | IN_ATTRIB | IN_MODIFY;
}
if ((events & kDelete) != 0) {
list_events |= IN_DELETE;
}
if ((events & kMove) != 0) {
list_events |= IN_MOVE;
}
const char* resolved_path = File::GetCanonicalPath(namespc, path);
path = resolved_path != nullptr ? resolved_path : path;
int path_id =
NO_RETRY_EXPECTED(inotify_add_watch(id, resolved_path, list_events));
if (path_id < 0) {
return -1;
}
return path_id;
}
void FileSystemWatcher::UnwatchPath(intptr_t id, intptr_t path_id) {
VOID_NO_RETRY_EXPECTED(inotify_rm_watch(id, path_id));
}
intptr_t FileSystemWatcher::GetSocketId(intptr_t id, intptr_t path_id) {
USE(path_id);
return id;
}
static int InotifyEventToMask(struct inotify_event* e) {
int mask = 0;
if ((e->mask & IN_CLOSE_WRITE) != 0 || (e->mask & IN_MODIFY) != 0) {
mask |= FileSystemWatcher::kModifyContent;
}
if ((e->mask & IN_ATTRIB) != 0) {
mask |= FileSystemWatcher::kModifyAttribute;
}
if ((e->mask & IN_CREATE) != 0) {
mask |= FileSystemWatcher::kCreate;
}
if ((e->mask & IN_MOVE) != 0) {
mask |= FileSystemWatcher::kMove;
}
if ((e->mask & IN_DELETE) != 0) {
mask |= FileSystemWatcher::kDelete;
}
if ((e->mask & (IN_DELETE_SELF | IN_MOVE_SELF)) != 0) {
mask |= FileSystemWatcher::kDeleteSelf;
}
if ((e->mask & IN_ISDIR) != 0) {
mask |= FileSystemWatcher::kIsDir;
}
return mask;
}
Dart_Handle FileSystemWatcher::ReadEvents(intptr_t id, intptr_t path_id) {
USE(path_id);
const intptr_t kEventSize = sizeof(struct inotify_event);
const intptr_t kBufferSize = kEventSize + NAME_MAX + 1;
uint8_t buffer[kBufferSize];
intptr_t bytes = TEMP_FAILURE_RETRY(read(id, buffer, kBufferSize));
if (bytes < 0) {
ASSERT(EAGAIN == EWOULDBLOCK);
if ((bytes == -1) && (errno == EWOULDBLOCK)) {
// see also SocketBase::Read
bytes = 0;
} else {
return DartUtils::NewDartOSError();
}
}
const intptr_t kMaxCount = bytes / kEventSize;
Dart_Handle events = Dart_NewList(kMaxCount);
intptr_t offset = 0;
intptr_t i = 0;
while (offset < bytes) {
struct inotify_event* e =
reinterpret_cast<struct inotify_event*>(buffer + offset);
if ((e->mask & IN_IGNORED) == 0) {
Dart_Handle event = Dart_NewList(5);
int mask = InotifyEventToMask(e);
Dart_ListSetAt(event, 0, Dart_NewInteger(mask));
Dart_ListSetAt(event, 1, Dart_NewInteger(e->cookie));
if (e->len > 0) {
Dart_Handle name = Dart_NewStringFromUTF8(
reinterpret_cast<uint8_t*>(e->name), strlen(e->name));
if (Dart_IsError(name)) {
return name;
}
Dart_ListSetAt(event, 2, name);
} else {
Dart_ListSetAt(event, 2, Dart_Null());
}
Dart_ListSetAt(event, 3, Dart_NewBoolean(e->mask & IN_MOVED_TO));
Dart_ListSetAt(event, 4, Dart_NewInteger(e->wd));
Dart_ListSetAt(events, i, event);
i++;
}
offset += kEventSize + e->len;
}
ASSERT(offset == bytes);
return events;
}
} // namespace bin
} // namespace dart
#endif // defined(DART_HOST_OS_ANDROID)

View file

@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h"
#if defined(DART_HOST_OS_LINUX)
#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
#include "bin/file_system_watcher.h"
@ -146,4 +146,4 @@ Dart_Handle FileSystemWatcher::ReadEvents(intptr_t id, intptr_t path_id) {
} // namespace bin
} // namespace dart
#endif // defined(DART_HOST_OS_LINUX)
#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)

View file

@ -1,10 +1,10 @@
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#if defined(ANDROID) && __ANDROID_API__ < 24
#include "bin/ifaddrs-android.h"
#include "bin/ifaddrs.h"
#include <errno.h>
#include <linux/netlink.h>

View file

@ -1,9 +1,13 @@
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#ifndef RUNTIME_BIN_IFADDRS_ANDROID_H_
#define RUNTIME_BIN_IFADDRS_ANDROID_H_
#ifndef RUNTIME_BIN_IFADDRS_H_
#define RUNTIME_BIN_IFADDRS_H_
// On Android getifaddrs API which is only supported directly by
// Bionic starting at API level 24 so we provide our own implementation
// which is API compatible. Otherwise we just include system ifaddrs.h
#if defined(ANDROID) && __ANDROID_API__ < 24
#include <sys/socket.h>
@ -30,5 +34,10 @@ int getifaddrs(struct ifaddrs** __list_ptr);
} // namespace bin
} // namespace dart
#else
#include <ifaddrs.h>
#endif // defined(ANDROID) && __ANDROID_API__ < 24
#endif // RUNTIME_BIN_IFADDRS_ANDROID_H_
#endif // RUNTIME_BIN_IFADDRS_H_

View file

@ -10,8 +10,6 @@ io_impl_sources = [
"console_win.cc",
"eventhandler.cc",
"eventhandler.h",
"eventhandler_android.cc",
"eventhandler_android.h",
"eventhandler_fuchsia.cc",
"eventhandler_fuchsia.h",
"eventhandler_linux.cc",
@ -22,22 +20,20 @@ io_impl_sources = [
"eventhandler_win.h",
"file_system_watcher.cc",
"file_system_watcher.h",
"file_system_watcher_android.cc",
"file_system_watcher_fuchsia.cc",
"file_system_watcher_linux.cc",
"file_system_watcher_macos.cc",
"file_system_watcher_win.cc",
"filter.cc",
"filter.h",
"ifaddrs-android.cc",
"ifaddrs-android.h",
"ifaddrs.cc",
"ifaddrs.h",
"io_service.cc",
"io_service.h",
"io_service_no_ssl.cc",
"io_service_no_ssl.h",
"namespace.cc",
"namespace.h",
"namespace_android.cc",
"namespace_fuchsia.cc",
"namespace_fuchsia.h",
"namespace_linux.cc",
@ -45,7 +41,6 @@ io_impl_sources = [
"namespace_win.cc",
"platform.cc",
"platform.h",
"platform_android.cc",
"platform_fuchsia.cc",
"platform_linux.cc",
"platform_macos.cc",
@ -53,7 +48,6 @@ io_impl_sources = [
"platform_win.cc",
"process.cc",
"process.h",
"process_android.cc",
"process_fuchsia.cc",
"process_linux.cc",
"process_macos.cc",
@ -67,18 +61,14 @@ io_impl_sources = [
"secure_socket_utils.h",
"security_context.cc",
"security_context.h",
"security_context_android.cc",
"security_context_fuchsia.cc",
"security_context_linux.cc",
"security_context_macos.cc",
"security_context_win.cc",
"socket.cc",
"socket.h",
"socket_android.cc",
"socket_base.cc",
"socket_base.h",
"socket_base_android.cc",
"socket_base_android.h",
"socket_base_fuchsia.cc",
"socket_base_fuchsia.h",
"socket_base_linux.cc",
@ -94,14 +84,12 @@ io_impl_sources = [
"socket_win.cc",
"stdio.cc",
"stdio.h",
"stdio_android.cc",
"stdio_fuchsia.cc",
"stdio_linux.cc",
"stdio_macos.cc",
"stdio_win.cc",
"sync_socket.cc",
"sync_socket.h",
"sync_socket_android.cc",
"sync_socket_fuchsia.cc",
"sync_socket_linux.cc",
"sync_socket_macos.cc",

View file

@ -1,159 +0,0 @@
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h"
#if defined(DART_HOST_OS_ANDROID)
#include "bin/namespace.h"
#include <errno.h>
#include <fcntl.h>
#include "bin/file.h"
#include "platform/signal_blocker.h"
#include "platform/text_buffer.h"
namespace dart {
namespace bin {
class NamespaceImpl {
public:
explicit NamespaceImpl(intptr_t rootfd) : rootfd_(rootfd), cwd_(strdup("/")) {
ASSERT(rootfd_ > 0);
cwdfd_ = dup(rootfd_);
ASSERT(cwdfd_ > 0);
}
explicit NamespaceImpl(const char* path)
: rootfd_(TEMP_FAILURE_RETRY(open(path, O_DIRECTORY))),
cwd_(strdup("/")) {
ASSERT(rootfd_ > 0);
cwdfd_ = dup(rootfd_);
ASSERT(cwdfd_ > 0);
}
~NamespaceImpl() {
NO_RETRY_EXPECTED(close(rootfd_));
free(cwd_);
NO_RETRY_EXPECTED(close(cwdfd_));
}
intptr_t rootfd() const { return rootfd_; }
char* cwd() const { return cwd_; }
intptr_t cwdfd() const { return cwdfd_; }
bool SetCwd(Namespace* namespc, const char* new_path) {
NamespaceScope ns(namespc, new_path);
const intptr_t new_cwdfd =
TEMP_FAILURE_RETRY(openat(ns.fd(), ns.path(), O_DIRECTORY));
if (new_cwdfd < 0) {
return false;
}
TextBuffer tbuf(PATH_MAX);
if (!File::IsAbsolutePath(new_path)) {
tbuf.AddString(cwd_);
}
tbuf.AddString(File::PathSeparator());
tbuf.AddString(ns.path());
// Normalize it.
char result[PATH_MAX];
const intptr_t result_len =
File::CleanUnixPath(tbuf.buffer(), result, PATH_MAX);
if (result_len < 0) {
errno = ENAMETOOLONG;
return false;
}
free(cwd_);
cwd_ = strdup(result);
close(cwdfd_);
cwdfd_ = new_cwdfd;
return true;
}
private:
intptr_t rootfd_; // dirfd for the namespace root.
char* cwd_; // cwd relative to the namespace.
intptr_t cwdfd_; // dirfd for the cwd.
DISALLOW_COPY_AND_ASSIGN(NamespaceImpl);
};
Namespace* Namespace::Create(intptr_t namespc) {
NamespaceImpl* namespc_impl = nullptr;
if (namespc != kNone) {
namespc_impl = new NamespaceImpl(namespc);
}
return new Namespace(namespc_impl);
}
Namespace* Namespace::Create(const char* path) {
return new Namespace(new NamespaceImpl(path));
}
Namespace::~Namespace() {
delete namespc_;
}
intptr_t Namespace::Default() {
return kNone;
}
const char* Namespace::GetCurrent(Namespace* namespc) {
if (Namespace::IsDefault(namespc)) {
char buffer[PATH_MAX];
if (getcwd(buffer, PATH_MAX) == nullptr) {
return nullptr;
}
return DartUtils::ScopedCopyCString(buffer);
}
const char* cwd = namespc->namespc()->cwd();
return cwd;
}
bool Namespace::SetCurrent(Namespace* namespc, const char* path) {
if (Namespace::IsDefault(namespc)) {
return (NO_RETRY_EXPECTED(chdir(path)) == 0);
}
return namespc->namespc()->SetCwd(namespc, path);
}
void Namespace::ResolvePath(Namespace* namespc,
const char* path,
intptr_t* dirfd,
const char** resolved_path) {
ASSERT(dirfd != nullptr);
ASSERT(resolved_path != nullptr);
if (Namespace::IsDefault(namespc)) {
*dirfd = AT_FDCWD;
*resolved_path = path;
return;
}
if (File::IsAbsolutePath(path)) {
*dirfd = namespc->namespc()->rootfd();
if (strcmp(path, File::PathSeparator()) == 0) {
// Change "/" to ".".
*resolved_path = ".";
} else {
// Otherwise strip off the leading "/".
*resolved_path = &path[1];
}
} else {
*dirfd = namespc->namespc()->cwdfd();
*resolved_path = path;
}
}
NamespaceScope::NamespaceScope(Namespace* namespc, const char* path) {
Namespace::ResolvePath(namespc, path, &fd_, &path_);
}
NamespaceScope::~NamespaceScope() {}
} // namespace bin
} // namespace dart
#endif // defined(DART_HOST_OS_ANDROID)

View file

@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h"
#if defined(DART_HOST_OS_LINUX)
#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
#include "bin/namespace.h"
@ -156,4 +156,4 @@ NamespaceScope::~NamespaceScope() {}
} // namespace bin
} // namespace dart
#endif // defined(DART_HOST_OS_LINUX)
#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)

View file

@ -1,208 +0,0 @@
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h"
#if defined(DART_HOST_OS_ANDROID)
#include "bin/platform.h"
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/resource.h>
#include <sys/system_properties.h>
#include <sys/utsname.h>
#include <unistd.h>
#include "bin/console.h"
#include "bin/file.h"
namespace dart {
namespace bin {
const char* Platform::executable_name_ = nullptr;
int Platform::script_index_ = 1;
char** Platform::argv_ = nullptr;
static const char* strcode(int si_signo, int si_code) {
#define CASE(signo, code) \
if (si_signo == signo && si_code == code) return #code;
CASE(SIGILL, ILL_ILLOPC);
CASE(SIGILL, ILL_ILLOPN);
CASE(SIGILL, ILL_ILLADR);
CASE(SIGILL, ILL_ILLTRP);
CASE(SIGILL, ILL_PRVOPC);
CASE(SIGILL, ILL_PRVREG);
CASE(SIGILL, ILL_COPROC);
CASE(SIGILL, ILL_BADSTK);
CASE(SIGSEGV, SEGV_MAPERR);
CASE(SIGSEGV, SEGV_ACCERR);
CASE(SIGBUS, BUS_ADRALN);
CASE(SIGBUS, BUS_ADRERR);
CASE(SIGBUS, BUS_OBJERR);
CASE(SIGBUS, BUS_MCEERR_AR);
CASE(SIGBUS, BUS_MCEERR_AO);
CASE(SIGTRAP, TRAP_BRKPT);
CASE(SIGTRAP, TRAP_TRACE);
#undef CASE
return "?";
}
static void segv_handler(int signal, siginfo_t* siginfo, void* context) {
Syslog::PrintErr(
"\n===== CRASH =====\n"
"si_signo=%s(%d), si_code=%s(%d), si_addr=%p\n",
strsignal(siginfo->si_signo), siginfo->si_signo,
strcode(siginfo->si_signo, siginfo->si_code), siginfo->si_code,
siginfo->si_addr);
Dart_DumpNativeStackTrace(context);
Dart_PrepareToAbort();
abort();
}
bool Platform::Initialize() {
// Turn off the signal handler for SIGPIPE as it causes the process
// to terminate on writing to a closed pipe. Without the signal
// handler error EPIPE is set instead.
struct sigaction act = {};
act.sa_handler = SIG_IGN;
if (sigaction(SIGPIPE, &act, nullptr) != 0) {
perror("Setting signal handler failed");
return false;
}
// tcsetattr raises SIGTTOU if we try to set console attributes when
// backgrounded, which suspends the process. Ignoring the signal prevents
// us from being suspended and lets us fail gracefully instead.
sigset_t signal_mask;
sigemptyset(&signal_mask);
sigaddset(&signal_mask, SIGTTOU);
if (sigprocmask(SIG_BLOCK, &signal_mask, nullptr) < 0) {
perror("Setting signal handler failed");
return false;
}
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = &segv_handler;
if (sigemptyset(&act.sa_mask) != 0) {
perror("sigemptyset() failed.");
return false;
}
if (sigaddset(&act.sa_mask, SIGPROF) != 0) {
perror("sigaddset() failed");
return false;
}
if (sigaction(SIGSEGV, &act, nullptr) != 0) {
perror("sigaction() failed.");
return false;
}
if (sigaction(SIGBUS, &act, nullptr) != 0) {
perror("sigaction() failed.");
return false;
}
if (sigaction(SIGTRAP, &act, nullptr) != 0) {
perror("sigaction() failed.");
return false;
}
if (sigaction(SIGILL, &act, nullptr) != 0) {
perror("sigaction() failed.");
return false;
}
return true;
}
int Platform::NumberOfProcessors() {
return sysconf(_SC_NPROCESSORS_ONLN);
}
const char* Platform::OperatingSystemVersion() {
char os_version[PROP_VALUE_MAX + 1];
int os_version_length =
__system_property_get("ro.build.display.id", os_version);
if (os_version_length == 0) {
return nullptr;
}
ASSERT(os_version_length <= PROP_VALUE_MAX);
char* result = reinterpret_cast<char*>(
Dart_ScopeAllocate((os_version_length + 1) * sizeof(result)));
strncpy(result, os_version, (os_version_length + 1));
return result;
}
const char* Platform::LibraryPrefix() {
return "lib";
}
const char* Platform::LibraryExtension() {
return "so";
}
const char* Platform::LocaleName() {
char* lang = getenv("LANG");
if (lang == nullptr) {
return "en_US";
}
return lang;
}
bool Platform::LocalHostname(char* buffer, intptr_t buffer_length) {
return gethostname(buffer, buffer_length) == 0;
}
char** Platform::Environment(intptr_t* count) {
// Using environ directly is only safe as long as we do not
// provide access to modifying environment variables.
intptr_t i = 0;
char** tmp = environ;
while (*(tmp++) != nullptr) {
i++;
}
*count = i;
char** result;
result = reinterpret_cast<char**>(Dart_ScopeAllocate(i * sizeof(*result)));
for (intptr_t current = 0; current < i; current++) {
result[current] = environ[current];
}
return result;
}
const char* Platform::GetExecutableName() {
return executable_name_;
}
const char* Platform::ResolveExecutablePath() {
return File::ReadLink("/proc/self/exe");
}
intptr_t Platform::ResolveExecutablePathInto(char* result, size_t result_size) {
return File::ReadLinkInto("/proc/self/exe", result, result_size);
}
void Platform::SetProcessName(const char* name) {
prctl(PR_SET_NAME, reinterpret_cast<unsigned long>(name), 0, 0, 0); // NOLINT
}
void Platform::Exit(int exit_code) {
Console::RestoreConfig();
Dart_PrepareToAbort();
exit(exit_code);
}
void Platform::_Exit(int exit_code) {
Console::RestoreConfig();
Dart_PrepareToAbort();
_exit(exit_code);
}
void Platform::SetCoreDumpResourceLimit(int value) {
rlimit limit = {static_cast<rlim_t>(value), static_cast<rlim_t>(value)};
setrlimit(RLIMIT_CORE, &limit);
}
} // namespace bin
} // namespace dart
#endif // defined(DART_HOST_OS_ANDROID)

View file

@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h"
#if defined(DART_HOST_OS_LINUX)
#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
#include "bin/platform.h"
@ -12,6 +12,9 @@
#include <string.h>
#include <sys/prctl.h>
#include <sys/resource.h>
#if defined(DART_HOST_OS_ANDROID)
#include <sys/system_properties.h>
#endif
#include <sys/utsname.h>
#include <unistd.h>
@ -118,25 +121,24 @@ int Platform::NumberOfProcessors() {
}
const char* Platform::OperatingSystemVersion() {
#if defined(DART_HOST_OS_ANDROID)
char os_version[PROP_VALUE_MAX + 1];
int os_version_length =
__system_property_get("ro.build.display.id", os_version);
if (os_version_length == 0) {
return nullptr;
}
os_version[Utils::Minimum(os_version_length, PROP_VALUE_MAX)] = '\0';
return DartUtils::ScopedCopyCString(os_version);
#else
struct utsname info;
int ret = uname(&info);
if (ret != 0) {
return nullptr;
}
const char* kFormat = "%s %s %s";
int len =
snprintf(nullptr, 0, kFormat, info.sysname, info.release, info.version);
if (len <= 0) {
return nullptr;
}
char* result = DartUtils::ScopedCString(len + 1);
ASSERT(result != nullptr);
len = snprintf(result, len + 1, kFormat, info.sysname, info.release,
info.version);
if (len <= 0) {
return nullptr;
}
return result;
return DartUtils::ScopedCStringFormatted("%s %s %s", info.sysname,
info.release, info.version);
#endif
}
const char* Platform::LibraryPrefix() {
@ -212,4 +214,4 @@ void Platform::SetCoreDumpResourceLimit(int value) {
} // namespace bin
} // namespace dart
#endif // defined(DART_HOST_OS_LINUX)
#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)

View file

@ -164,18 +164,7 @@ static const char* VersionNumber() {
if (!GetCurrentVersionDWord(L"CurrentMinorVersionNumber", &minor)) {
return nullptr;
}
const char* kFormat = "%d.%d";
int len = snprintf(nullptr, 0, kFormat, major, minor);
if (len < 0) {
return nullptr;
}
char* result = DartUtils::ScopedCString(len + 1);
ASSERT(result != nullptr);
len = snprintf(result, len + 1, kFormat, major, minor);
if (len < 0) {
return nullptr;
}
return result;
return DartUtils::ScopedCStringFormatted("%d.%d", major, minor);
}
const char* Platform::OperatingSystemVersion() {

File diff suppressed because it is too large Load diff

View file

@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h"
#if defined(DART_HOST_OS_LINUX)
#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
#include "bin/process.h"
@ -1162,4 +1162,4 @@ void Process::Cleanup() {
} // namespace bin
} // namespace dart
#endif // defined(DART_HOST_OS_LINUX)
#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)

View file

@ -1,69 +0,0 @@
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#if !defined(DART_IO_SECURE_SOCKET_DISABLED)
#include "platform/globals.h"
#if defined(DART_HOST_OS_ANDROID)
#include "bin/security_context.h"
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/x509.h>
#include "bin/directory.h"
#include "bin/file.h"
#include "bin/secure_socket_filter.h"
#include "bin/secure_socket_utils.h"
#include "platform/syslog.h"
namespace dart {
namespace bin {
// The security context won't necessarily use the compiled-in root certificates,
// but since there is no way to update the size of the allocation after creating
// the weak persistent handle, we assume that it will. Note that when the
// root certs aren't compiled in, |root_certificates_pem_length| is 0.
const intptr_t SSLCertContext::kApproximateSize =
sizeof(SSLCertContext) + root_certificates_pem_length;
void SSLCertContext::TrustBuiltinRoots() {
// First, try to use locations specified on the command line.
if (root_certs_file() != nullptr) {
LoadRootCertFile(root_certs_file());
return;
}
if (root_certs_cache() != nullptr) {
LoadRootCertCache(root_certs_cache());
return;
}
// On Android, we don't compile in the trusted root certificates. Instead,
// we use the directory of trusted certificates already present on the device.
// This saves ~240KB from the size of the binary. This has the drawback that
// SSL_do_handshake will synchronously hit the filesystem looking for root
// certs during its trust evaluation. We call SSL_do_handshake directly from
// the Dart thread so that Dart code can be invoked from the "bad certificate"
// callback called by SSL_do_handshake.
const char* android_cacerts = "/system/etc/security/cacerts";
LoadRootCertCache(android_cacerts);
return;
}
void SSLCertContext::RegisterCallbacks(SSL* ssl) {
// No callbacks to register for implementations using BoringSSL's built-in
// verification mechanism.
}
TrustEvaluateHandlerFunc SSLCertContext::GetTrustEvaluateHandler() const {
return nullptr;
}
} // namespace bin
} // namespace dart
#endif // defined(DART_HOST_OS_ANDROID)
#endif // !defined(DART_IO_SECURE_SOCKET_DISABLED)

View file

@ -5,7 +5,7 @@
#if !defined(DART_IO_SECURE_SOCKET_DISABLED)
#include "platform/globals.h"
#if defined(DART_HOST_OS_LINUX)
#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
#include "bin/security_context.h"
@ -42,9 +42,21 @@ void SSLCertContext::TrustBuiltinRoots() {
if (bypass_trusting_system_roots()) {
if (SSL_LOG_STATUS) {
Syslog::Print("Bypass trusting Linux built-in roots\n");
Syslog::Print("Bypass trusting built-in system roots\n");
}
} else {
#if defined(DART_HOST_OS_ANDROID)
// On Android, we don't compile in the trusted root certificates. Instead,
// we use the directory of trusted certificates already present on the
// device. This saves ~240KB from the size of the binary. This has the
// drawback that SSL_do_handshake will synchronously hit the filesystem
// looking for root certs during its trust evaluation. We call
// SSL_do_handshake directly from the Dart thread so that Dart code can be
// invoked from the "bad certificate" callback called by SSL_do_handshake.
const char* android_cacerts = "/system/etc/security/cacerts";
LoadRootCertCache(android_cacerts);
return;
#else
// On Linux, we use the compiled-in trusted certs as a last resort. First,
// we try to find the trusted certs in various standard locations. A good
// discussion of the complexities of this endeavor can be found here:
@ -61,14 +73,16 @@ void SSLCertContext::TrustBuiltinRoots() {
LoadRootCertCache(cachedir);
return;
}
#endif
}
// Fall back on the compiled-in certs if the standard locations don't exist,
// or we aren't on Linux.
#if defined(DART_HOST_OS_LINUX)
// Fall back on the compiled-in certs if the standard locations don't exist.
if (SSL_LOG_STATUS) {
Syslog::Print("Trusting compiled-in roots\n");
}
AddCompiledInCerts();
#endif
}
void SSLCertContext::RegisterCallbacks(SSL* ssl) {
@ -83,6 +97,6 @@ TrustEvaluateHandlerFunc SSLCertContext::GetTrustEvaluateHandler() const {
} // namespace bin
} // namespace dart
#endif // defined(DART_HOST_OS_LINUX)
#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
#endif // !defined(DART_IO_SECURE_SOCKET_DISABLED)

View file

@ -1,283 +0,0 @@
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h"
#if defined(DART_HOST_OS_ANDROID)
#include "bin/socket.h"
#include <errno.h> // NOLINT
#include "bin/fdutils.h"
#include "platform/signal_blocker.h"
#include "platform/syslog.h"
namespace dart {
namespace bin {
Socket::Socket(intptr_t fd)
: ReferenceCounted(),
fd_(fd),
isolate_port_(Dart_GetMainPortId()),
port_(ILLEGAL_PORT),
udp_receive_buffer_(nullptr) {}
void Socket::CloseFd() {
SetClosedFd();
}
void Socket::SetClosedFd() {
fd_ = kClosedFd;
}
static intptr_t Create(const RawAddr& addr) {
intptr_t fd;
fd = NO_RETRY_EXPECTED(socket(addr.ss.ss_family, SOCK_STREAM, 0));
if (fd < 0) {
return -1;
}
if (!FDUtils::SetCloseOnExec(fd) || !FDUtils::SetNonBlocking(fd)) {
FDUtils::SaveErrorAndClose(fd);
return -1;
}
return fd;
}
static intptr_t Connect(intptr_t fd, const RawAddr& addr) {
intptr_t result = TEMP_FAILURE_RETRY(
connect(fd, &addr.addr, SocketAddress::GetAddrLength(addr)));
if ((result == 0) || (errno == EINPROGRESS)) {
return fd;
}
FDUtils::SaveErrorAndClose(fd);
return -1;
}
intptr_t Socket::CreateConnect(const RawAddr& addr) {
intptr_t fd = Create(addr);
if (fd < 0) {
return fd;
}
return Connect(fd, addr);
}
intptr_t Socket::CreateUnixDomainConnect(const RawAddr& addr) {
intptr_t fd = Create(addr);
if (fd < 0) {
return fd;
}
intptr_t result = TEMP_FAILURE_RETRY(connect(
fd, (struct sockaddr*)&addr.un, SocketAddress::GetAddrLength(addr)));
if (result == 0 || errno == EAGAIN) {
return fd;
}
FDUtils::SaveErrorAndClose(fd);
return -1;
}
intptr_t Socket::CreateBindConnect(const RawAddr& addr,
const RawAddr& source_addr) {
intptr_t fd = Create(addr);
if (fd < 0) {
return fd;
}
intptr_t result = TEMP_FAILURE_RETRY(
bind(fd, &source_addr.addr, SocketAddress::GetAddrLength(source_addr)));
if (result != 0) {
FDUtils::SaveErrorAndClose(fd);
return -1;
}
return Connect(fd, addr);
}
intptr_t Socket::CreateUnixDomainBindConnect(const RawAddr& addr,
const RawAddr& source_addr) {
intptr_t fd = Create(addr);
if (fd < 0) {
return fd;
}
intptr_t result = TEMP_FAILURE_RETRY(
bind(fd, &source_addr.addr, SocketAddress::GetAddrLength(source_addr)));
if (result != 0) {
FDUtils::SaveErrorAndClose(fd);
return -1;
}
result = TEMP_FAILURE_RETRY(connect(fd, (struct sockaddr*)&addr.un,
SocketAddress::GetAddrLength(addr)));
if (result == 0 || errno == EAGAIN) {
return fd;
}
FDUtils::SaveErrorAndClose(fd);
return -1;
}
intptr_t Socket::CreateBindDatagram(const RawAddr& addr,
bool reuseAddress,
bool reusePort,
int ttl) {
intptr_t fd;
fd = NO_RETRY_EXPECTED(socket(addr.addr.sa_family, SOCK_DGRAM, IPPROTO_UDP));
if (fd < 0) {
return -1;
}
if (!FDUtils::SetCloseOnExec(fd)) {
FDUtils::SaveErrorAndClose(fd);
return -1;
}
if (reuseAddress) {
int optval = 1;
VOID_NO_RETRY_EXPECTED(
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)));
}
if (reusePort) {
// ignore reusePort - not supported on this platform.
Syslog::PrintErr(
"Dart Socket ERROR: %s:%d: `reusePort` not supported for "
"Android.",
__FILE__, __LINE__);
}
if (!SocketBase::SetMulticastHops(fd,
addr.addr.sa_family == AF_INET
? SocketAddress::TYPE_IPV4
: SocketAddress::TYPE_IPV6,
ttl)) {
FDUtils::SaveErrorAndClose(fd);
return -1;
}
if (NO_RETRY_EXPECTED(
bind(fd, &addr.addr, SocketAddress::GetAddrLength(addr))) < 0) {
FDUtils::SaveErrorAndClose(fd);
return -1;
}
if (!FDUtils::SetNonBlocking(fd)) {
FDUtils::SaveErrorAndClose(fd);
return -1;
}
return fd;
}
intptr_t ServerSocket::CreateBindListen(const RawAddr& addr,
intptr_t backlog,
bool v6_only) {
intptr_t fd;
fd = NO_RETRY_EXPECTED(socket(addr.ss.ss_family, SOCK_STREAM, 0));
if (fd < 0) {
return -1;
}
if (!FDUtils::SetCloseOnExec(fd)) {
FDUtils::SaveErrorAndClose(fd);
return -1;
}
int optval = 1;
VOID_NO_RETRY_EXPECTED(
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)));
if (addr.ss.ss_family == AF_INET6) {
optval = v6_only ? 1 : 0;
VOID_NO_RETRY_EXPECTED(
setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &optval, sizeof(optval)));
}
if (NO_RETRY_EXPECTED(
bind(fd, &addr.addr, SocketAddress::GetAddrLength(addr))) < 0) {
FDUtils::SaveErrorAndClose(fd);
return -1;
}
// Test for invalid socket port 65535 (some browsers disallow it).
if ((SocketAddress::GetAddrPort(addr)) == 0 &&
(SocketBase::GetPort(fd) == 65535)) {
// Don't close the socket until we have created a new socket, ensuring
// that we do not get the bad port number again.
intptr_t new_fd = CreateBindListen(addr, backlog, v6_only);
FDUtils::SaveErrorAndClose(fd);
return new_fd;
}
if (NO_RETRY_EXPECTED(listen(fd, backlog > 0 ? backlog : SOMAXCONN)) != 0) {
FDUtils::SaveErrorAndClose(fd);
return -1;
}
if (!FDUtils::SetNonBlocking(fd)) {
FDUtils::SaveErrorAndClose(fd);
return -1;
}
return fd;
}
intptr_t ServerSocket::CreateUnixDomainBindListen(const RawAddr& addr,
intptr_t backlog) {
intptr_t fd = Create(addr);
if (NO_RETRY_EXPECTED(bind(fd, (struct sockaddr*)&addr.un,
SocketAddress::GetAddrLength(addr))) < 0) {
FDUtils::SaveErrorAndClose(fd);
return -1;
}
if (NO_RETRY_EXPECTED(listen(fd, backlog > 0 ? backlog : SOMAXCONN)) != 0) {
FDUtils::SaveErrorAndClose(fd);
return -1;
}
return fd;
}
bool ServerSocket::StartAccept(intptr_t fd) {
USE(fd);
return true;
}
static bool IsTemporaryAcceptError(int error) {
// On Android a number of protocol errors should be treated as EAGAIN.
// These are the ones for TCP/IP.
return (error == EAGAIN) || (error == ENETDOWN) || (error == EPROTO) ||
(error == ENOPROTOOPT) || (error == EHOSTDOWN) || (error == ENONET) ||
(error == EHOSTUNREACH) || (error == EOPNOTSUPP) ||
(error == ENETUNREACH);
}
intptr_t ServerSocket::Accept(intptr_t fd) {
intptr_t socket;
struct sockaddr clientaddr;
socklen_t addrlen = sizeof(clientaddr);
socket = TEMP_FAILURE_RETRY(accept(fd, &clientaddr, &addrlen));
if (socket == -1) {
if (IsTemporaryAcceptError(errno)) {
// We need to signal to the caller that this is actually not an
// error. We got woken up from the poll on the listening socket,
// but there is no connection ready to be accepted.
ASSERT(kTemporaryFailure != -1);
socket = kTemporaryFailure;
}
} else {
if (!FDUtils::SetCloseOnExec(socket)) {
FDUtils::SaveErrorAndClose(socket);
return -1;
}
if (!FDUtils::SetNonBlocking(socket)) {
FDUtils::SaveErrorAndClose(socket);
return -1;
}
}
return socket;
}
} // namespace bin
} // namespace dart
#endif // defined(DART_HOST_OS_ANDROID)

View file

@ -7,11 +7,9 @@
#include "platform/globals.h"
// Declare the OS-specific types ahead of defining the generic class.
#if defined(DART_HOST_OS_ANDROID)
#include "bin/socket_base_android.h"
#elif defined(DART_HOST_OS_FUCHSIA)
#if defined(DART_HOST_OS_FUCHSIA)
#include "bin/socket_base_fuchsia.h"
#elif defined(DART_HOST_OS_LINUX)
#elif defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
#include "bin/socket_base_linux.h"
#elif defined(DART_HOST_OS_MACOS)
#include "bin/socket_base_macos.h"

View file

@ -1,505 +0,0 @@
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h"
#if defined(DART_HOST_OS_ANDROID)
#include "bin/socket_base.h"
#include <errno.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <netinet/tcp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include "bin/fdutils.h"
#include "bin/file.h"
#include "bin/ifaddrs-android.h"
#include "bin/socket_base_android.h"
#include "platform/signal_blocker.h"
namespace dart {
namespace bin {
SocketAddress::SocketAddress(struct sockaddr* sa, bool unnamed_unix_socket) {
if (unnamed_unix_socket) {
// This is an unnamed unix domain socket.
as_string_[0] = 0;
} else if (sa->sa_family == AF_UNIX) {
struct sockaddr_un* un = ((struct sockaddr_un*)sa);
memmove(as_string_, un->sun_path, sizeof(un->sun_path));
} else {
ASSERT(INET6_ADDRSTRLEN >= INET_ADDRSTRLEN);
if (!SocketBase::FormatNumericAddress(*reinterpret_cast<RawAddr*>(sa),
as_string_, INET6_ADDRSTRLEN)) {
as_string_[0] = 0;
}
}
socklen_t salen =
GetAddrLength(*reinterpret_cast<RawAddr*>(sa), unnamed_unix_socket);
memmove(reinterpret_cast<void*>(&addr_), sa, salen);
}
bool SocketBase::Initialize() {
// Nothing to do on Android.
return true;
}
bool SocketBase::FormatNumericAddress(const RawAddr& addr,
char* address,
int len) {
socklen_t salen = SocketAddress::GetAddrLength(addr);
return (NO_RETRY_EXPECTED(getnameinfo(&addr.addr, salen, address, len,
nullptr, 0, NI_NUMERICHOST)) == 0);
}
bool SocketBase::IsBindError(intptr_t error_number) {
return error_number == EADDRINUSE || error_number == EADDRNOTAVAIL ||
error_number == EINVAL;
}
intptr_t SocketBase::Available(intptr_t fd) {
return FDUtils::AvailableBytes(fd);
}
intptr_t SocketBase::Read(intptr_t fd,
void* buffer,
intptr_t num_bytes,
SocketOpKind sync) {
ASSERT(fd >= 0);
ssize_t read_bytes = TEMP_FAILURE_RETRY(read(fd, buffer, num_bytes));
ASSERT(EAGAIN == EWOULDBLOCK);
if ((sync == kAsync) && (read_bytes == -1) && (errno == EWOULDBLOCK)) {
// If the read would block we need to retry and therefore return 0
// as the number of bytes written.
read_bytes = 0;
}
return read_bytes;
}
intptr_t SocketBase::RecvFrom(intptr_t fd,
void* buffer,
intptr_t num_bytes,
RawAddr* addr,
SocketOpKind sync) {
ASSERT(fd >= 0);
socklen_t addr_len = sizeof(addr->ss);
ssize_t read_bytes = TEMP_FAILURE_RETRY(
recvfrom(fd, buffer, num_bytes, 0, &addr->addr, &addr_len));
if ((sync == kAsync) && (read_bytes == -1) && (errno == EWOULDBLOCK)) {
// If the read would block we need to retry and therefore return 0
// as the number of bytes written.
read_bytes = 0;
}
return read_bytes;
}
bool SocketControlMessage::is_file_descriptors_control_message() {
return false;
}
intptr_t SocketBase::ReceiveMessage(intptr_t fd,
void* buffer,
int64_t* p_buffer_num_bytes,
SocketControlMessage** p_messages,
SocketOpKind sync,
OSError* p_oserror) {
errno = ENOSYS;
return -1;
}
bool SocketBase::AvailableDatagram(intptr_t fd,
void* buffer,
intptr_t num_bytes) {
ASSERT(fd >= 0);
ssize_t read_bytes = TEMP_FAILURE_RETRY(
recvfrom(fd, buffer, num_bytes, MSG_PEEK, nullptr, nullptr));
return read_bytes >= 0;
}
intptr_t SocketBase::WriteImpl(intptr_t fd,
const void* buffer,
intptr_t num_bytes,
SocketOpKind sync) {
ASSERT(fd >= 0);
return TEMP_FAILURE_RETRY(write(fd, buffer, num_bytes));
}
intptr_t SocketBase::SendTo(intptr_t fd,
const void* buffer,
intptr_t num_bytes,
const RawAddr& addr,
SocketOpKind sync) {
ASSERT(fd >= 0);
ssize_t written_bytes =
TEMP_FAILURE_RETRY(sendto(fd, buffer, num_bytes, 0, &addr.addr,
SocketAddress::GetAddrLength(addr)));
ASSERT(EAGAIN == EWOULDBLOCK);
if ((sync == kAsync) && (written_bytes == -1) && (errno == EWOULDBLOCK)) {
// If the would block we need to retry and therefore return 0 as
// the number of bytes written.
written_bytes = 0;
}
return written_bytes;
}
intptr_t SocketBase::SendMessage(intptr_t fd,
void* buffer,
size_t num_bytes,
SocketControlMessage* messages,
intptr_t num_messages,
SocketOpKind sync,
OSError* p_oserror) {
errno = ENOSYS;
return -1;
}
bool SocketBase::GetSocketName(intptr_t fd, SocketAddress* p_sa) {
ASSERT(fd >= 0);
ASSERT(p_sa != nullptr);
RawAddr raw;
socklen_t size = sizeof(raw);
if (NO_RETRY_EXPECTED(getsockname(fd, &raw.addr, &size))) {
return false;
}
// sockaddr_un contains sa_family_t sun_family and char[] sun_path.
// If size is the size of sa_family_t, this is an unnamed socket and
// sun_path contains garbage.
new (p_sa) SocketAddress(&raw.addr,
/*unnamed_unix_socket=*/size == sizeof(sa_family_t));
return true;
}
intptr_t SocketBase::GetPort(intptr_t fd) {
ASSERT(fd >= 0);
RawAddr raw;
socklen_t size = sizeof(raw);
if (NO_RETRY_EXPECTED(getsockname(fd, &raw.addr, &size))) {
return 0;
}
return SocketAddress::GetAddrPort(raw);
}
SocketAddress* SocketBase::GetRemotePeer(intptr_t fd, intptr_t* port) {
ASSERT(fd >= 0);
RawAddr raw;
socklen_t size = sizeof(raw);
if (NO_RETRY_EXPECTED(getpeername(fd, &raw.addr, &size))) {
return nullptr;
}
// sockaddr_un contains sa_family_t sun_family and char[] sun_path.
// If size is the size of sa_family_t, this is an unnamed socket and
// sun_path contains garbage.
if (size == sizeof(sa_family_t)) {
*port = 0;
return new SocketAddress(&raw.addr, true);
}
*port = SocketAddress::GetAddrPort(raw);
return new SocketAddress(&raw.addr);
}
void SocketBase::GetError(intptr_t fd, OSError* os_error) {
int errorNumber;
socklen_t len = sizeof(errorNumber);
getsockopt(fd, SOL_SOCKET, SO_ERROR, reinterpret_cast<void*>(&errorNumber),
&len);
os_error->SetCodeAndMessage(OSError::kSystem, errorNumber);
}
int SocketBase::GetType(intptr_t fd) {
struct stat buf;
int result = fstat(fd, &buf);
if (result == -1) {
return -1;
}
if (S_ISCHR(buf.st_mode)) {
return File::kTerminal;
}
if (S_ISFIFO(buf.st_mode)) {
return File::kPipe;
}
if (S_ISREG(buf.st_mode)) {
return File::kFile;
}
return File::kOther;
}
intptr_t SocketBase::GetStdioHandle(intptr_t num) {
return num;
}
AddressList<SocketAddress>* SocketBase::LookupAddress(const char* host,
int type,
OSError** os_error) {
// Perform a name lookup for a host name.
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = SocketAddress::FromType(type);
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_ADDRCONFIG;
hints.ai_protocol = IPPROTO_TCP;
struct addrinfo* info = nullptr;
int status = getaddrinfo(host, nullptr, &hints, &info);
if (status != 0) {
// We failed, try without AI_ADDRCONFIG. This can happen when looking up
// e.g. '::1', when there are no IPv6 addresses.
hints.ai_flags = 0;
status = getaddrinfo(host, nullptr, &hints, &info);
if (status != 0) {
ASSERT(*os_error == nullptr);
*os_error =
new OSError(status, gai_strerror(status), OSError::kGetAddressInfo);
return nullptr;
}
}
intptr_t count = 0;
for (struct addrinfo* c = info; c != nullptr; c = c->ai_next) {
if ((c->ai_family == AF_INET) || (c->ai_family == AF_INET6)) {
count++;
}
}
intptr_t i = 0;
AddressList<SocketAddress>* addresses = new AddressList<SocketAddress>(count);
for (struct addrinfo* c = info; c != nullptr; c = c->ai_next) {
if ((c->ai_family == AF_INET) || (c->ai_family == AF_INET6)) {
addresses->SetAt(i, new SocketAddress(c->ai_addr));
i++;
}
}
freeaddrinfo(info);
return addresses;
}
bool SocketBase::ReverseLookup(const RawAddr& addr,
char* host,
intptr_t host_len,
OSError** os_error) {
ASSERT(host_len >= NI_MAXHOST);
int status = NO_RETRY_EXPECTED(
getnameinfo(&addr.addr, SocketAddress::GetAddrLength(addr), host,
host_len, nullptr, 0, NI_NAMEREQD));
if (status != 0) {
ASSERT(*os_error == nullptr);
*os_error =
new OSError(status, gai_strerror(status), OSError::kGetAddressInfo);
return false;
}
return true;
}
bool SocketBase::ParseAddress(int type, const char* address, RawAddr* addr) {
int result;
if (type == SocketAddress::TYPE_IPV4) {
result = inet_pton(AF_INET, address, &addr->in.sin_addr);
} else {
ASSERT(type == SocketAddress::TYPE_IPV6);
result = inet_pton(AF_INET6, address, &addr->in6.sin6_addr);
}
return (result == 1);
}
bool SocketBase::RawAddrToString(RawAddr* addr, char* str) {
if (addr->addr.sa_family == AF_INET) {
return inet_ntop(AF_INET, &addr->in.sin_addr, str, INET_ADDRSTRLEN) !=
nullptr;
} else {
ASSERT(addr->addr.sa_family == AF_INET6);
return inet_ntop(AF_INET6, &addr->in6.sin6_addr, str, INET6_ADDRSTRLEN) !=
nullptr;
}
}
static bool ShouldIncludeIfaAddrs(struct ifaddrs* ifa, int lookup_family) {
if (ifa->ifa_addr == nullptr) {
// OpenVPN's virtual device tun0.
return false;
}
int family = ifa->ifa_addr->sa_family;
return ((lookup_family == family) ||
(((lookup_family == AF_UNSPEC) &&
((family == AF_INET) || (family == AF_INET6)))));
}
AddressList<InterfaceSocketAddress>* SocketBase::ListInterfaces(
int type,
OSError** os_error) {
struct ifaddrs* ifaddr;
int status = NO_RETRY_EXPECTED(getifaddrs(&ifaddr));
if (status != 0) {
ASSERT(*os_error == nullptr);
*os_error =
new OSError(status, gai_strerror(status), OSError::kGetAddressInfo);
return nullptr;
}
int lookup_family = SocketAddress::FromType(type);
intptr_t count = 0;
for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
if (ShouldIncludeIfaAddrs(ifa, lookup_family)) {
count++;
}
}
AddressList<InterfaceSocketAddress>* addresses =
new AddressList<InterfaceSocketAddress>(count);
int i = 0;
for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
if (ShouldIncludeIfaAddrs(ifa, lookup_family)) {
char* ifa_name = DartUtils::ScopedCopyCString(ifa->ifa_name);
addresses->SetAt(
i, new InterfaceSocketAddress(ifa->ifa_addr, ifa_name,
if_nametoindex(ifa->ifa_name)));
i++;
}
}
freeifaddrs(ifaddr);
return addresses;
}
void SocketBase::Close(intptr_t fd) {
ASSERT(fd >= 0);
close(fd);
}
bool SocketBase::GetNoDelay(intptr_t fd, bool* enabled) {
int on;
socklen_t len = sizeof(on);
int err = NO_RETRY_EXPECTED(getsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
reinterpret_cast<void*>(&on), &len));
if (err == 0) {
*enabled = (on == 1);
}
return (err == 0);
}
bool SocketBase::SetNoDelay(intptr_t fd, bool enabled) {
int on = enabled ? 1 : 0;
return NO_RETRY_EXPECTED(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
reinterpret_cast<char*>(&on),
sizeof(on))) == 0;
}
bool SocketBase::GetMulticastLoop(intptr_t fd,
intptr_t protocol,
bool* enabled) {
uint8_t on;
socklen_t len = sizeof(on);
int level = protocol == SocketAddress::TYPE_IPV4 ? IPPROTO_IP : IPPROTO_IPV6;
int optname = protocol == SocketAddress::TYPE_IPV4 ? IP_MULTICAST_LOOP
: IPV6_MULTICAST_LOOP;
if (NO_RETRY_EXPECTED(getsockopt(fd, level, optname,
reinterpret_cast<char*>(&on), &len)) == 0) {
*enabled = (on == 1);
return true;
}
return false;
}
bool SocketBase::SetMulticastLoop(intptr_t fd,
intptr_t protocol,
bool enabled) {
int on = enabled ? 1 : 0;
int level = protocol == SocketAddress::TYPE_IPV4 ? IPPROTO_IP : IPPROTO_IPV6;
int optname = protocol == SocketAddress::TYPE_IPV4 ? IP_MULTICAST_LOOP
: IPV6_MULTICAST_LOOP;
return NO_RETRY_EXPECTED(setsockopt(
fd, level, optname, reinterpret_cast<char*>(&on), sizeof(on))) ==
0;
}
bool SocketBase::GetMulticastHops(intptr_t fd, intptr_t protocol, int* value) {
uint8_t v;
socklen_t len = sizeof(v);
int level = protocol == SocketAddress::TYPE_IPV4 ? IPPROTO_IP : IPPROTO_IPV6;
int optname = protocol == SocketAddress::TYPE_IPV4 ? IP_MULTICAST_TTL
: IPV6_MULTICAST_HOPS;
if (NO_RETRY_EXPECTED(getsockopt(fd, level, optname,
reinterpret_cast<char*>(&v), &len)) == 0) {
*value = v;
return true;
}
return false;
}
bool SocketBase::SetMulticastHops(intptr_t fd, intptr_t protocol, int value) {
int v = value;
int level = protocol == SocketAddress::TYPE_IPV4 ? IPPROTO_IP : IPPROTO_IPV6;
int optname = protocol == SocketAddress::TYPE_IPV4 ? IP_MULTICAST_TTL
: IPV6_MULTICAST_HOPS;
return NO_RETRY_EXPECTED(setsockopt(
fd, level, optname, reinterpret_cast<char*>(&v), sizeof(v))) == 0;
}
bool SocketBase::GetBroadcast(intptr_t fd, bool* enabled) {
int on;
socklen_t len = sizeof(on);
int err = NO_RETRY_EXPECTED(getsockopt(fd, SOL_SOCKET, SO_BROADCAST,
reinterpret_cast<char*>(&on), &len));
if (err == 0) {
*enabled = (on == 1);
}
return (err == 0);
}
bool SocketBase::SetBroadcast(intptr_t fd, bool enabled) {
int on = enabled ? 1 : 0;
return NO_RETRY_EXPECTED(setsockopt(fd, SOL_SOCKET, SO_BROADCAST,
reinterpret_cast<char*>(&on),
sizeof(on))) == 0;
}
bool SocketBase::SetOption(intptr_t fd,
int level,
int option,
const char* data,
int length) {
return NO_RETRY_EXPECTED(setsockopt(fd, level, option, data, length)) == 0;
}
bool SocketBase::GetOption(intptr_t fd,
int level,
int option,
char* data,
unsigned int* length) {
socklen_t optlen = static_cast<socklen_t>(*length);
auto result = NO_RETRY_EXPECTED(getsockopt(fd, level, option, data, &optlen));
*length = static_cast<unsigned int>(optlen);
return result == 0;
}
bool SocketBase::JoinMulticast(intptr_t fd,
const RawAddr& addr,
const RawAddr&,
int interfaceIndex) {
int proto = (addr.addr.sa_family == AF_INET) ? IPPROTO_IP : IPPROTO_IPV6;
struct group_req mreq;
mreq.gr_interface = interfaceIndex;
memmove(&mreq.gr_group, &addr.ss, SocketAddress::GetAddrLength(addr));
return NO_RETRY_EXPECTED(
setsockopt(fd, proto, MCAST_JOIN_GROUP, &mreq, sizeof(mreq))) == 0;
}
bool SocketBase::LeaveMulticast(intptr_t fd,
const RawAddr& addr,
const RawAddr&,
int interfaceIndex) {
int proto = (addr.addr.sa_family == AF_INET) ? IPPROTO_IP : IPPROTO_IPV6;
struct group_req mreq;
mreq.gr_interface = interfaceIndex;
memmove(&mreq.gr_group, &addr.ss, SocketAddress::GetAddrLength(addr));
return NO_RETRY_EXPECTED(setsockopt(fd, proto, MCAST_LEAVE_GROUP, &mreq,
sizeof(mreq))) == 0;
}
} // namespace bin
} // namespace dart
#endif // defined(DART_HOST_OS_ANDROID)

View file

@ -1,17 +0,0 @@
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#ifndef RUNTIME_BIN_SOCKET_BASE_ANDROID_H_
#define RUNTIME_BIN_SOCKET_BASE_ANDROID_H_
#if !defined(RUNTIME_BIN_SOCKET_BASE_H_)
#error Do not include socket_base_android.h directly. Use socket_base.h.
#endif
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/un.h>
#endif // RUNTIME_BIN_SOCKET_BASE_ANDROID_H_

View file

@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h"
#if defined(DART_HOST_OS_LINUX)
#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
#include "bin/socket_base.h"
@ -145,4 +145,4 @@ bool SocketBase::LeaveMulticast(intptr_t fd,
} // namespace bin
} // namespace dart
#endif // defined(DART_HOST_OS_LINUX)
#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)

View file

@ -3,11 +3,11 @@
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h"
#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS)
#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || \
defined(DART_HOST_OS_MACOS)
#include "bin/socket_base.h"
#include <errno.h> // NOLINT
#include <ifaddrs.h> // NOLINT
#include <net/if.h> // NOLINT
#include <netinet/tcp.h> // NOLINT
#include <stdio.h> // NOLINT
@ -18,6 +18,7 @@
#include "bin/fdutils.h"
#include "bin/file.h"
#include "bin/ifaddrs.h"
#include "bin/socket_base_macos.h"
#include "platform/signal_blocker.h"

View file

@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h"
#if defined(DART_HOST_OS_LINUX)
#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
#include "bin/socket.h"
@ -134,7 +134,7 @@ intptr_t Socket::CreateBindDatagram(const RawAddr& addr,
}
if (reusePort) {
#ifdef SO_REUSEPORT // Not all Linux versions support this.
#if !defined(DART_HOST_OS_ANDROID) && defined(SO_REUSEPORT)
int optval = 1;
int reuse_port_success =
setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
@ -149,12 +149,12 @@ intptr_t Socket::CreateBindDatagram(const RawAddr& addr,
Syslog::PrintErr("Dart Socket ERROR: %s:%d: %s.", __FILE__, __LINE__,
Utils::StrError(errno, error_buf, kBufferSize));
}
#else // !defined SO_REUSEPORT
#else // defined(DART_HOST_OS_ANDROID) || !defined(SO_REUSEPORT)
Syslog::PrintErr(
"Dart Socket ERROR: %s:%d: `reusePort` not available on this Linux "
"version.",
"Dart Socket ERROR: %s:%d: `reusePort` not supported on this "
"platform.",
__FILE__, __LINE__);
#endif // SO_REUSEPORT
#endif // !defined(DART_HOST_OS_ANDROID) && defined(SO_REUSEPORT)
}
if (!SocketBase::SetMulticastHops(fd,
@ -277,4 +277,4 @@ intptr_t ServerSocket::Accept(intptr_t fd) {
} // namespace bin
} // namespace dart
#endif // defined(DART_HOST_OS_LINUX)
#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)

View file

@ -1,139 +0,0 @@
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h"
#if defined(DART_HOST_OS_ANDROID)
#include "bin/stdio.h"
#include <errno.h> // NOLINT
#include <sys/ioctl.h> // NOLINT
#include <termios.h> // NOLINT
#include "bin/fdutils.h"
#include "platform/signal_blocker.h"
namespace dart {
namespace bin {
bool Stdin::ReadByte(intptr_t fd, int* byte) {
unsigned char b;
ssize_t s = TEMP_FAILURE_RETRY(read(fd, &b, 1));
if (s < 0) {
return false;
}
*byte = (s == 0) ? -1 : b;
return true;
}
bool Stdin::GetEchoMode(intptr_t fd, bool* enabled) {
struct termios term;
int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term));
if (status != 0) {
return false;
}
*enabled = ((term.c_lflag & ECHO) != 0);
return true;
}
bool Stdin::SetEchoMode(intptr_t fd, bool enabled) {
struct termios term;
int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term));
if (status != 0) {
return false;
}
if (enabled) {
term.c_lflag |= ECHO;
} else {
term.c_lflag &= ~(ECHO);
}
status = NO_RETRY_EXPECTED(tcsetattr(fd, TCSANOW, &term));
return (status == 0);
}
bool Stdin::GetEchoNewlineMode(intptr_t fd, bool* enabled) {
struct termios term;
int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term));
if (status != 0) {
return false;
}
*enabled = ((term.c_lflag & ECHONL) != 0);
return true;
}
bool Stdin::SetEchoNewlineMode(intptr_t fd, bool enabled) {
struct termios term;
int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term));
if (status != 0) {
return false;
}
if (enabled) {
term.c_lflag |= ECHONL;
} else {
term.c_lflag &= ~(ECHONL);
}
status = NO_RETRY_EXPECTED(tcsetattr(fd, TCSANOW, &term));
return (status == 0);
}
bool Stdin::GetLineMode(intptr_t fd, bool* enabled) {
struct termios term;
int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term));
if (status != 0) {
return false;
}
*enabled = ((term.c_lflag & ICANON) != 0);
return true;
}
bool Stdin::SetLineMode(intptr_t fd, bool enabled) {
struct termios term;
int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term));
if (status != 0) {
return false;
}
if (enabled) {
term.c_lflag |= ICANON;
} else {
term.c_lflag &= ~(ICANON);
}
status = NO_RETRY_EXPECTED(tcsetattr(fd, TCSANOW, &term));
return (status == 0);
}
static bool TermIsKnownToSupportAnsi() {
const char* term = getenv("TERM");
if (term == nullptr) {
return false;
}
return strstr(term, "xterm") != nullptr ||
strstr(term, "screen") != nullptr || strstr(term, "rxvt") != nullptr;
}
bool Stdin::AnsiSupported(intptr_t fd, bool* supported) {
*supported = isatty(fd) && TermIsKnownToSupportAnsi();
return true;
}
bool Stdout::GetTerminalSize(intptr_t fd, int size[2]) {
struct winsize w;
int status = NO_RETRY_EXPECTED(ioctl(fd, TIOCGWINSZ, &w));
if ((status == 0) && ((w.ws_col != 0) || (w.ws_row != 0))) {
size[0] = w.ws_col;
size[1] = w.ws_row;
return true;
}
return false;
}
bool Stdout::AnsiSupported(intptr_t fd, bool* supported) {
*supported = isatty(fd) && TermIsKnownToSupportAnsi();
return true;
}
} // namespace bin
} // namespace dart
#endif // defined(DART_HOST_OS_ANDROID)

View file

@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h"
#if defined(DART_HOST_OS_LINUX)
#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
#include "bin/stdio.h"
@ -136,4 +136,4 @@ bool Stdout::AnsiSupported(intptr_t fd, bool* supported) {
} // namespace bin
} // namespace dart
#endif // defined(DART_HOST_OS_LINUX)
#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)

View file

@ -1,92 +0,0 @@
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h"
#if defined(DART_HOST_OS_ANDROID)
#include "bin/sync_socket.h"
#include <errno.h> // NOLINT
#include "bin/fdutils.h"
#include "bin/socket_base.h"
#include "platform/signal_blocker.h"
namespace dart {
namespace bin {
bool SynchronousSocket::Initialize() {
// Nothing to do on Android.
return true;
}
static intptr_t Create(const RawAddr& addr) {
intptr_t fd;
intptr_t type = SOCK_STREAM;
fd = NO_RETRY_EXPECTED(socket(addr.ss.ss_family, type, 0));
if (fd < 0) {
return -1;
}
return fd;
}
static intptr_t Connect(intptr_t fd, const RawAddr& addr) {
intptr_t result = TEMP_FAILURE_RETRY(
connect(fd, &addr.addr, SocketAddress::GetAddrLength(addr)));
if (result == 0) {
return fd;
}
ASSERT(errno != EINPROGRESS);
FDUtils::SaveErrorAndClose(fd);
return -1;
}
intptr_t SynchronousSocket::CreateConnect(const RawAddr& addr) {
intptr_t fd = Create(addr);
if (fd < 0) {
return fd;
}
return Connect(fd, addr);
}
intptr_t SynchronousSocket::Available(intptr_t fd) {
return SocketBase::Available(fd);
}
intptr_t SynchronousSocket::GetPort(intptr_t fd) {
return SocketBase::GetPort(fd);
}
SocketAddress* SynchronousSocket::GetRemotePeer(intptr_t fd, intptr_t* port) {
return SocketBase::GetRemotePeer(fd, port);
}
intptr_t SynchronousSocket::Read(intptr_t fd,
void* buffer,
intptr_t num_bytes) {
return SocketBase::Read(fd, buffer, num_bytes, SocketBase::kSync);
}
intptr_t SynchronousSocket::Write(intptr_t fd,
const void* buffer,
intptr_t num_bytes) {
return SocketBase::Write(fd, buffer, num_bytes, SocketBase::kSync);
}
void SynchronousSocket::ShutdownRead(intptr_t fd) {
VOID_NO_RETRY_EXPECTED(shutdown(fd, SHUT_RD));
}
void SynchronousSocket::ShutdownWrite(intptr_t fd) {
VOID_NO_RETRY_EXPECTED(shutdown(fd, SHUT_WR));
}
void SynchronousSocket::Close(intptr_t fd) {
return SocketBase::Close(fd);
}
} // namespace bin
} // namespace dart
#endif // defined(DART_HOST_OS_ANDROID)

View file

@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h"
#if defined(DART_HOST_OS_LINUX)
#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
#include "bin/sync_socket.h"
@ -89,4 +89,4 @@ void SynchronousSocket::Close(intptr_t fd) {
} // namespace bin
} // namespace dart
#endif // defined(DART_HOST_OS_LINUX)
#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)

View file

@ -18,11 +18,9 @@ class Monitor;
// Declare the OS-specific types ahead of defining the generic classes.
#if defined(DART_USE_ABSL)
#include "bin/thread_absl.h"
#elif defined(DART_HOST_OS_ANDROID)
#include "bin/thread_android.h"
#elif defined(DART_HOST_OS_FUCHSIA)
#include "bin/thread_fuchsia.h"
#elif defined(DART_HOST_OS_LINUX)
#elif defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
#include "bin/thread_linux.h"
#elif defined(DART_HOST_OS_MACOS)
#include "bin/thread_macos.h"

View file

@ -1,277 +0,0 @@
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h"
#if defined(DART_HOST_OS_ANDROID) && !defined(DART_USE_ABSL)
#include "bin/thread.h"
#include "bin/thread_android.h"
#include <errno.h> // NOLINT
#include <sys/time.h> // NOLINT
#include "platform/assert.h"
#include "platform/utils.h"
namespace dart {
namespace bin {
#define VALIDATE_PTHREAD_RESULT(result) \
if (result != 0) { \
const int kBufferSize = 1024; \
char error_message[kBufferSize]; \
Utils::StrError(result, error_message, kBufferSize); \
FATAL("pthread error: %d (%s)", result, error_message); \
}
#ifdef DEBUG
#define RETURN_ON_PTHREAD_FAILURE(result) \
if (result != 0) { \
const int kBufferSize = 1024; \
char error_message[kBufferSize]; \
Utils::StrError(result, error_message, kBufferSize); \
fprintf(stderr, "%s:%d: pthread error: %d (%s)\n", __FILE__, __LINE__, \
result, error_message); \
return result; \
}
#else
#define RETURN_ON_PTHREAD_FAILURE(result) \
if (result != 0) { \
return result; \
}
#endif
static void ComputeTimeSpecMicros(struct timespec* ts, int64_t micros) {
struct timeval tv;
int64_t secs = micros / kMicrosecondsPerSecond;
int64_t remaining_micros = (micros - (secs * kMicrosecondsPerSecond));
int result = gettimeofday(&tv, nullptr);
ASSERT(result == 0);
ts->tv_sec = tv.tv_sec + secs;
ts->tv_nsec = (tv.tv_usec + remaining_micros) * kNanosecondsPerMicrosecond;
if (ts->tv_nsec >= kNanosecondsPerSecond) {
ts->tv_sec += 1;
ts->tv_nsec -= kNanosecondsPerSecond;
}
}
class ThreadStartData {
public:
ThreadStartData(const char* name,
Thread::ThreadStartFunction function,
uword parameter)
: name_(name), function_(function), parameter_(parameter) {}
const char* name() const { return name_; }
Thread::ThreadStartFunction function() const { return function_; }
uword parameter() const { return parameter_; }
private:
const char* name_;
Thread::ThreadStartFunction function_;
uword parameter_;
DISALLOW_COPY_AND_ASSIGN(ThreadStartData);
};
// Dispatch to the thread start function provided by the caller. This trampoline
// is used to ensure that the thread is properly destroyed if the thread just
// exits.
static void* ThreadStart(void* data_ptr) {
ThreadStartData* data = reinterpret_cast<ThreadStartData*>(data_ptr);
const char* name = data->name();
Thread::ThreadStartFunction function = data->function();
uword parameter = data->parameter();
delete data;
// Set the thread name. There is 16 bytes limit on the name (including \0).
// pthread_setname_np ignores names that are too long rather than truncating.
char truncated_name[16];
snprintf(truncated_name, sizeof(truncated_name), "%s", name);
pthread_setname_np(pthread_self(), truncated_name);
// Call the supplied thread start function handing it its parameters.
function(parameter);
return nullptr;
}
int Thread::Start(const char* name,
ThreadStartFunction function,
uword parameter) {
pthread_attr_t attr;
int result = pthread_attr_init(&attr);
RETURN_ON_PTHREAD_FAILURE(result);
result = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
RETURN_ON_PTHREAD_FAILURE(result);
result = pthread_attr_setstacksize(&attr, Thread::GetMaxStackSize());
RETURN_ON_PTHREAD_FAILURE(result);
ThreadStartData* data = new ThreadStartData(name, function, parameter);
pthread_t tid;
result = pthread_create(&tid, &attr, ThreadStart, data);
RETURN_ON_PTHREAD_FAILURE(result);
result = pthread_attr_destroy(&attr);
RETURN_ON_PTHREAD_FAILURE(result);
return 0;
}
const ThreadId Thread::kInvalidThreadId = static_cast<ThreadId>(0);
intptr_t Thread::GetMaxStackSize() {
const int kStackSize = (128 * kWordSize * KB);
return kStackSize;
}
ThreadId Thread::GetCurrentThreadId() {
return gettid();
}
bool Thread::Compare(ThreadId a, ThreadId b) {
return (a == b);
}
Mutex::Mutex() {
pthread_mutexattr_t attr;
int result = pthread_mutexattr_init(&attr);
VALIDATE_PTHREAD_RESULT(result);
#if defined(DEBUG)
result = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
VALIDATE_PTHREAD_RESULT(result);
#endif // defined(DEBUG)
result = pthread_mutex_init(data_.mutex(), &attr);
// Verify that creating a pthread_mutex succeeded.
VALIDATE_PTHREAD_RESULT(result);
result = pthread_mutexattr_destroy(&attr);
VALIDATE_PTHREAD_RESULT(result);
}
Mutex::~Mutex() {
int result = pthread_mutex_destroy(data_.mutex());
// Verify that the pthread_mutex was destroyed.
VALIDATE_PTHREAD_RESULT(result);
}
void Mutex::Lock() {
int result = pthread_mutex_lock(data_.mutex());
// Specifically check for dead lock to help debugging.
ASSERT(result != EDEADLK);
ASSERT(result == 0); // Verify no other errors.
// TODO(iposva): Do we need to track lock owners?
}
bool Mutex::TryLock() {
int result = pthread_mutex_trylock(data_.mutex());
// Return false if the lock is busy and locking failed.
if (result == EBUSY) {
return false;
}
ASSERT(result == 0); // Verify no other errors.
// TODO(iposva): Do we need to track lock owners?
return true;
}
void Mutex::Unlock() {
// TODO(iposva): Do we need to track lock owners?
int result = pthread_mutex_unlock(data_.mutex());
// Specifically check for wrong thread unlocking to aid debugging.
ASSERT(result != EPERM);
ASSERT(result == 0); // Verify no other errors.
}
Monitor::Monitor() {
pthread_mutexattr_t mutex_attr;
int result = pthread_mutexattr_init(&mutex_attr);
VALIDATE_PTHREAD_RESULT(result);
#if defined(DEBUG)
result = pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_ERRORCHECK);
VALIDATE_PTHREAD_RESULT(result);
#endif // defined(DEBUG)
result = pthread_mutex_init(data_.mutex(), &mutex_attr);
VALIDATE_PTHREAD_RESULT(result);
result = pthread_mutexattr_destroy(&mutex_attr);
VALIDATE_PTHREAD_RESULT(result);
pthread_condattr_t cond_attr;
result = pthread_condattr_init(&cond_attr);
VALIDATE_PTHREAD_RESULT(result);
result = pthread_cond_init(data_.cond(), &cond_attr);
VALIDATE_PTHREAD_RESULT(result);
result = pthread_condattr_destroy(&cond_attr);
VALIDATE_PTHREAD_RESULT(result);
}
Monitor::~Monitor() {
int result = pthread_mutex_destroy(data_.mutex());
VALIDATE_PTHREAD_RESULT(result);
result = pthread_cond_destroy(data_.cond());
VALIDATE_PTHREAD_RESULT(result);
}
void Monitor::Enter() {
int result = pthread_mutex_lock(data_.mutex());
VALIDATE_PTHREAD_RESULT(result);
// TODO(iposva): Do we need to track lock owners?
}
void Monitor::Exit() {
// TODO(iposva): Do we need to track lock owners?
int result = pthread_mutex_unlock(data_.mutex());
VALIDATE_PTHREAD_RESULT(result);
}
Monitor::WaitResult Monitor::Wait(int64_t millis) {
return WaitMicros(millis * kMicrosecondsPerMillisecond);
}
Monitor::WaitResult Monitor::WaitMicros(int64_t micros) {
// TODO(iposva): Do we need to track lock owners?
Monitor::WaitResult retval = kNotified;
if (micros == kNoTimeout) {
// Wait forever.
int result = pthread_cond_wait(data_.cond(), data_.mutex());
VALIDATE_PTHREAD_RESULT(result);
} else {
struct timespec ts;
ComputeTimeSpecMicros(&ts, micros);
int result = pthread_cond_timedwait(data_.cond(), data_.mutex(), &ts);
ASSERT((result == 0) || (result == ETIMEDOUT));
if (result == ETIMEDOUT) {
retval = kTimedOut;
}
}
return retval;
}
void Monitor::Notify() {
// TODO(iposva): Do we need to track lock owners?
int result = pthread_cond_signal(data_.cond());
VALIDATE_PTHREAD_RESULT(result);
}
void Monitor::NotifyAll() {
// TODO(iposva): Do we need to track lock owners?
int result = pthread_cond_broadcast(data_.cond());
VALIDATE_PTHREAD_RESULT(result);
}
} // namespace bin
} // namespace dart
#endif // defined(DART_HOST_OS_ANDROID) && !defined(DART_USE_ABSL)

View file

@ -1,57 +0,0 @@
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#ifndef RUNTIME_BIN_THREAD_ANDROID_H_
#define RUNTIME_BIN_THREAD_ANDROID_H_
#if !defined(RUNTIME_BIN_THREAD_H_)
#error Do not include thread_android.h directly; use thread.h instead.
#endif
#include <pthread.h>
#include "platform/assert.h"
#include "platform/globals.h"
namespace dart {
namespace bin {
typedef pthread_t ThreadId;
class MutexData {
private:
MutexData() {}
~MutexData() {}
pthread_mutex_t* mutex() { return &mutex_; }
pthread_mutex_t mutex_;
friend class Mutex;
DISALLOW_ALLOCATION();
DISALLOW_COPY_AND_ASSIGN(MutexData);
};
class MonitorData {
private:
MonitorData() {}
~MonitorData() {}
pthread_mutex_t* mutex() { return &mutex_; }
pthread_cond_t* cond() { return &cond_; }
pthread_mutex_t mutex_;
pthread_cond_t cond_;
friend class Monitor;
DISALLOW_ALLOCATION();
DISALLOW_COPY_AND_ASSIGN(MonitorData);
};
} // namespace bin
} // namespace dart
#endif // RUNTIME_BIN_THREAD_ANDROID_H_

View file

@ -3,7 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h"
#if defined(DART_HOST_OS_LINUX) && !defined(DART_USE_ABSL)
#if (defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)) && \
!defined(DART_USE_ABSL)
#include "bin/thread.h"
#include "bin/thread_linux.h"
@ -277,4 +278,5 @@ void Monitor::NotifyAll() {
} // namespace bin
} // namespace dart
#endif // defined(DART_HOST_OS_LINUX) && !defined(DART_USE_ABSL)
#endif // (defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)) && \
// !defined(DART_USE_ABSL)

View file

@ -1,114 +0,0 @@
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h"
#if defined(DART_HOST_OS_ANDROID)
#include <errno.h> // NOLINT
#include <netdb.h> // NOLINT
#include <sys/time.h> // NOLINT
#include <time.h> // NOLINT
#include "bin/utils.h"
#include "platform/assert.h"
#include "platform/utils.h"
namespace dart {
namespace bin {
OSError::OSError() : sub_system_(kSystem), code_(0), message_(nullptr) {
Reload();
}
void OSError::Reload() {
SetCodeAndMessage(kSystem, errno);
}
void OSError::SetCodeAndMessage(SubSystem sub_system, int code) {
set_sub_system(sub_system);
set_code(code);
if (sub_system == kSystem) {
const int kBufferSize = 1024;
char error_message[kBufferSize];
Utils::StrError(code, error_message, kBufferSize);
SetMessage(error_message);
} else if (sub_system == kGetAddressInfo) {
SetMessage(gai_strerror(code));
} else {
UNREACHABLE();
}
}
const char* StringUtils::ConsoleStringToUtf8(const char* str,
intptr_t len,
intptr_t* result_len) {
return nullptr;
}
const char* StringUtils::Utf8ToConsoleString(const char* utf8,
intptr_t len,
intptr_t* result_len) {
return nullptr;
}
char* StringUtils::ConsoleStringToUtf8(char* str,
intptr_t len,
intptr_t* result_len) {
return nullptr;
}
char* StringUtils::Utf8ToConsoleString(char* utf8,
intptr_t len,
intptr_t* result_len) {
return nullptr;
}
bool ShellUtils::GetUtf8Argv(int argc, char** argv) {
return false;
}
void TimerUtils::InitOnce() {}
int64_t TimerUtils::GetCurrentMonotonicMillis() {
return GetCurrentMonotonicMicros() / 1000;
}
int64_t TimerUtils::GetCurrentMonotonicMicros() {
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
UNREACHABLE();
return 0;
}
// Convert to microseconds.
int64_t result = ts.tv_sec;
result *= kMicrosecondsPerSecond;
result += (ts.tv_nsec / kNanosecondsPerMicrosecond);
return result;
}
void TimerUtils::Sleep(int64_t millis) {
struct timespec req; // requested.
struct timespec rem; // remainder.
int64_t micros = millis * kMicrosecondsPerMillisecond;
int64_t seconds = micros / kMicrosecondsPerSecond;
micros = micros - seconds * kMicrosecondsPerSecond;
int64_t nanos = micros * kNanosecondsPerMicrosecond;
req.tv_sec = seconds;
req.tv_nsec = nanos;
while (true) {
int r = nanosleep(&req, &rem);
if (r == 0) {
break;
}
// We should only ever see an interrupt error.
ASSERT(errno == EINTR);
// Copy remainder into requested and repeat.
req = rem;
}
}
} // namespace bin
} // namespace dart
#endif // defined(DART_HOST_OS_ANDROID)

View file

@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h"
#if defined(DART_HOST_OS_LINUX)
#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
#include <errno.h> // NOLINT
#include <netdb.h> // NOLINT
@ -110,4 +110,4 @@ void TimerUtils::Sleep(int64_t millis) {
} // namespace bin
} // namespace dart
#endif // defined(DART_HOST_OS_LINUX)
#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)