| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "dart-pkg/zircon/sdk_ext/system.h" |
| |
| #include <array> |
| |
| #include <ddk/device.h> |
| #include <fcntl.h> |
| #include <fuchsia/device/manager/cpp/fidl.h> |
| #include <lib/fdio/directory.h> |
| #include <lib/fdio/io.h> |
| #include <lib/fdio/limits.h> |
| #include <lib/fdio/namespace.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| #include <zircon/process.h> |
| #include <zircon/processargs.h> |
| |
| #include "lib/fsl/io/fd.h" |
| #include "src/lib/files/unique_fd.h" |
| #include "third_party/tonic/dart_binding_macros.h" |
| #include "third_party/tonic/dart_class_library.h" |
| |
| using tonic::ToDart; |
| |
| namespace zircon { |
| namespace dart { |
| |
| namespace { |
| |
| constexpr char kGetSizeResult[] = "GetSizeResult"; |
| constexpr char kHandlePairResult[] = "HandlePairResult"; |
| constexpr char kHandleResult[] = "HandleResult"; |
| constexpr char kReadResult[] = "ReadResult"; |
| constexpr char kWriteResult[] = "WriteResult"; |
| constexpr char kFromFileResult[] = "FromFileResult"; |
| constexpr char kMapResult[] = "MapResult"; |
| |
| class ByteDataScope { |
| public: |
| explicit ByteDataScope(Dart_Handle dart_handle) : dart_handle_(dart_handle) { |
| Acquire(); |
| } |
| |
| explicit ByteDataScope(size_t size) { |
| dart_handle_ = Dart_NewTypedData(Dart_TypedData_kByteData, size); |
| FXL_DCHECK(!tonic::LogIfError(dart_handle_)); |
| Acquire(); |
| FXL_DCHECK(size == size_); |
| } |
| |
| ~ByteDataScope() { |
| if (is_valid_) { |
| Release(); |
| } |
| } |
| |
| void* data() const { return data_; } |
| size_t size() const { return size_; } |
| Dart_Handle dart_handle() const { return dart_handle_; } |
| bool is_valid() const { return is_valid_; } |
| |
| void Release() { |
| FXL_DCHECK(is_valid_); |
| Dart_Handle result = Dart_TypedDataReleaseData(dart_handle_); |
| tonic::LogIfError(result); |
| is_valid_ = false; |
| data_ = nullptr; |
| size_ = 0; |
| } |
| |
| private: |
| void Acquire() { |
| FXL_DCHECK(size_ == 0); |
| FXL_DCHECK(data_ == nullptr); |
| FXL_DCHECK(!is_valid_); |
| |
| Dart_TypedData_Type type; |
| intptr_t size; |
| Dart_Handle result = |
| Dart_TypedDataAcquireData(dart_handle_, &type, &data_, &size); |
| is_valid_ = |
| !tonic::LogIfError(result) && type == Dart_TypedData_kByteData && data_; |
| if (is_valid_) { |
| size_ = size; |
| } else { |
| size_ = 0; |
| } |
| } |
| |
| Dart_Handle dart_handle_; |
| bool is_valid_ = false; |
| size_t size_ = 0; |
| void* data_ = nullptr; |
| }; |
| |
| Dart_Handle MakeHandleList(const std::vector<zx_handle_t>& in_handles) { |
| tonic::DartClassLibrary& class_library = |
| tonic::DartState::Current()->class_library(); |
| Dart_Handle handle_type = class_library.GetClass("zircon", "Handle"); |
| Dart_Handle list = Dart_NewListOfType(handle_type, in_handles.size()); |
| if (Dart_IsError(list)) |
| return list; |
| for (size_t i = 0; i < in_handles.size(); i++) { |
| Dart_Handle result = |
| Dart_ListSetAt(list, i, ToDart(Handle::Create(in_handles[i]))); |
| if (Dart_IsError(result)) |
| return result; |
| } |
| return list; |
| } |
| |
| template <class... Args> |
| Dart_Handle ConstructDartObject(const char* class_name, Args&&... args) { |
| tonic::DartClassLibrary& class_library = |
| tonic::DartState::Current()->class_library(); |
| Dart_Handle type = |
| Dart_HandleFromPersistent(class_library.GetClass("zircon", class_name)); |
| FXL_DCHECK(!tonic::LogIfError(type)); |
| |
| std::array<Dart_Handle, sizeof...(Args)> args_array{ |
| {std::forward<Args>(args)...}}; |
| Dart_Handle object = |
| Dart_New(type, Dart_EmptyString(), sizeof...(Args), args_array.data()); |
| FXL_DCHECK(!tonic::LogIfError(object)); |
| return object; |
| } |
| |
| fxl::UniqueFD FdFromPath(std::string path) { |
| // Grab the fdio_ns_t* out of the isolate. |
| Dart_Handle zircon_lib = Dart_LookupLibrary(ToDart("dart:zircon")); |
| FXL_DCHECK(!tonic::LogIfError(zircon_lib)); |
| Dart_Handle namespace_type = |
| Dart_GetType(zircon_lib, ToDart("_Namespace"), 0, nullptr); |
| FXL_DCHECK(!tonic::LogIfError(namespace_type)); |
| Dart_Handle namespace_field = |
| Dart_GetField(namespace_type, ToDart("_namespace")); |
| FXL_DCHECK(!tonic::LogIfError(namespace_field)); |
| uint64_t fdio_ns_ptr; |
| Dart_Handle result = Dart_IntegerToUint64(namespace_field, &fdio_ns_ptr); |
| FXL_DCHECK(!tonic::LogIfError(result)); |
| |
| // Get a VMO for the file. |
| fdio_ns_t* ns = reinterpret_cast<fdio_ns_t*>(fdio_ns_ptr); |
| fxl::UniqueFD dirfd(fdio_ns_opendir(ns)); |
| if (!dirfd.is_valid()) |
| return fxl::UniqueFD(); |
| |
| const char* c_path = path.c_str(); |
| if (path.length() > 0 && c_path[0] == '/') |
| c_path = &c_path[1]; |
| return fxl::UniqueFD(openat(dirfd.get(), c_path, O_RDONLY)); |
| } |
| |
| } // namespace |
| |
| IMPLEMENT_WRAPPERTYPEINFO(zircon, System); |
| |
| Dart_Handle System::ChannelCreate(uint32_t options) { |
| zx_handle_t out0 = 0, out1 = 0; |
| zx_status_t status = zx_channel_create(options, &out0, &out1); |
| if (status != ZX_OK) { |
| return ConstructDartObject(kHandlePairResult, ToDart(status)); |
| } else { |
| return ConstructDartObject(kHandlePairResult, ToDart(status), |
| ToDart(Handle::Create(out0)), |
| ToDart(Handle::Create(out1))); |
| } |
| } |
| |
| zx_status_t System::Reboot() { |
| zx::channel local, remote; |
| auto status = zx::channel::create(0, &local, &remote); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| const std::string service = std::string{"/svc/"} + |
| fuchsia::device::manager::Administrator::Name_; |
| status = fdio_service_connect(service.c_str(), remote.get()); |
| if (status != ZX_OK) { |
| printf("failed to connect to service %s: %d\n", service.c_str(), status); |
| return status; |
| } |
| |
| zx_status_t call_status; |
| fuchsia::device::manager::Administrator_SyncProxy administrator(std::move(local)); |
| status = administrator.Suspend(DEVICE_SUSPEND_FLAG_REBOOT, &call_status); |
| if (status != ZX_OK || call_status != ZX_OK) { |
| printf("Call to %s failed: ret: %d remote: %d\n", service.c_str(), status, call_status); |
| } |
| |
| return status != ZX_OK ? status : call_status; |
| } |
| |
| Dart_Handle System::ChannelFromFile(std::string path) { |
| fxl::UniqueFD fd = FdFromPath(path); |
| if (!fd.is_valid()) { |
| return ConstructDartObject(kHandleResult, ToDart(ZX_ERR_IO)); |
| } |
| |
| // Get channel from fd. |
| zx::channel channel = fsl::CloneChannelFromFileDescriptor(fd.get()); |
| if (!channel) { |
| return ConstructDartObject(kHandleResult, ToDart(ZX_ERR_IO)); |
| } |
| |
| return ConstructDartObject(kHandleResult, ToDart(ZX_OK), |
| ToDart(Handle::Create(channel.release()))); |
| } |
| |
| zx_status_t System::ChannelWrite(fxl::RefPtr<Handle> channel, |
| const tonic::DartByteData& data, |
| std::vector<Handle*> handles) { |
| if (!channel || !channel->is_valid()) { |
| data.Release(); |
| return ZX_ERR_BAD_HANDLE; |
| } |
| |
| std::vector<zx_handle_t> zx_handles; |
| for (Handle* handle : handles) { |
| zx_handles.push_back(handle->handle()); |
| } |
| |
| zx_status_t status = zx_channel_write(channel->handle(), 0, data.data(), |
| data.length_in_bytes(), |
| zx_handles.data(), zx_handles.size()); |
| // Handles are always consumed. |
| for (Handle* handle : handles) { |
| handle->ReleaseHandle(); |
| } |
| |
| data.Release(); |
| return status; |
| } |
| |
| Dart_Handle System::ChannelQueryAndRead(fxl::RefPtr<Handle> channel) { |
| if (!channel || !channel->is_valid()) { |
| return ConstructDartObject(kReadResult, ToDart(ZX_ERR_BAD_HANDLE)); |
| } |
| |
| uint32_t actual_bytes = 0; |
| uint32_t actual_handles = 0; |
| |
| // Query the size of the next message. |
| zx_status_t status = zx_channel_read(channel->handle(), 0, nullptr, nullptr, |
| 0, 0, &actual_bytes, &actual_handles); |
| if (status != ZX_ERR_BUFFER_TOO_SMALL) { |
| // An empty message or an error. |
| return ConstructDartObject(kReadResult, ToDart(status)); |
| } |
| |
| // Allocate space for the bytes and handles. |
| ByteDataScope bytes(actual_bytes); |
| FXL_DCHECK(bytes.is_valid()); |
| std::vector<zx_handle_t> handles(actual_handles); |
| |
| // Make the call to actually get the message. |
| status = zx_channel_read(channel->handle(), 0, bytes.data(), handles.data(), |
| bytes.size(), handles.size(), &actual_bytes, |
| &actual_handles); |
| FXL_DCHECK(status != ZX_OK || bytes.size() == actual_bytes); |
| |
| bytes.Release(); |
| |
| if (status == ZX_OK) { |
| FXL_DCHECK(handles.size() == actual_handles); |
| |
| // return a ReadResult object. |
| return ConstructDartObject(kReadResult, ToDart(status), bytes.dart_handle(), |
| ToDart(actual_bytes), MakeHandleList(handles)); |
| } else { |
| return ConstructDartObject(kReadResult, ToDart(status)); |
| } |
| } |
| |
| Dart_Handle System::EventpairCreate(uint32_t options) { |
| zx_handle_t out0 = 0, out1 = 0; |
| zx_status_t status = zx_eventpair_create(0, &out0, &out1); |
| if (status != ZX_OK) { |
| return ConstructDartObject(kHandlePairResult, ToDart(status)); |
| } else { |
| return ConstructDartObject(kHandlePairResult, ToDart(status), |
| ToDart(Handle::Create(out0)), |
| ToDart(Handle::Create(out1))); |
| } |
| } |
| |
| Dart_Handle System::SocketCreate(uint32_t options) { |
| zx_handle_t out0 = 0, out1 = 0; |
| zx_status_t status = zx_socket_create(options, &out0, &out1); |
| if (status != ZX_OK) { |
| return ConstructDartObject(kHandlePairResult, ToDart(status)); |
| } else { |
| return ConstructDartObject(kHandlePairResult, ToDart(status), |
| ToDart(Handle::Create(out0)), |
| ToDart(Handle::Create(out1))); |
| } |
| } |
| |
| Dart_Handle System::SocketWrite(fxl::RefPtr<Handle> socket, |
| const tonic::DartByteData& data, |
| int options) { |
| if (!socket || !socket->is_valid()) { |
| data.Release(); |
| return ConstructDartObject(kWriteResult, ToDart(ZX_ERR_BAD_HANDLE)); |
| } |
| |
| size_t actual; |
| zx_status_t status = zx_socket_write(socket->handle(), options, data.data(), |
| data.length_in_bytes(), &actual); |
| data.Release(); |
| return ConstructDartObject(kWriteResult, ToDart(status), ToDart(actual)); |
| } |
| |
| Dart_Handle System::SocketRead(fxl::RefPtr<Handle> socket, size_t size) { |
| if (!socket || !socket->is_valid()) { |
| return ConstructDartObject(kReadResult, ToDart(ZX_ERR_BAD_HANDLE)); |
| } |
| |
| ByteDataScope bytes(size); |
| size_t actual; |
| zx_status_t status = |
| zx_socket_read(socket->handle(), 0, bytes.data(), size, &actual); |
| bytes.Release(); |
| if (status == ZX_OK) { |
| FXL_DCHECK(actual <= size); |
| return ConstructDartObject(kReadResult, ToDart(status), bytes.dart_handle(), |
| ToDart(actual)); |
| } |
| |
| return ConstructDartObject(kReadResult, ToDart(status)); |
| } |
| |
| Dart_Handle System::VmoCreate(uint64_t size, uint32_t options) { |
| zx_handle_t vmo = ZX_HANDLE_INVALID; |
| zx_status_t status = zx_vmo_create(size, options, &vmo); |
| if (status != ZX_OK) { |
| return ConstructDartObject(kHandleResult, ToDart(status)); |
| } else { |
| return ConstructDartObject(kHandleResult, ToDart(status), |
| ToDart(Handle::Create(vmo))); |
| } |
| } |
| |
| Dart_Handle System::VmoFromFile(std::string path) { |
| fxl::UniqueFD fd = FdFromPath(path); |
| if (!fd.is_valid()) |
| return ConstructDartObject(kFromFileResult, ToDart(ZX_ERR_IO)); |
| |
| struct stat stat_struct; |
| if (fstat(fd.get(), &stat_struct) == -1) |
| return ConstructDartObject(kFromFileResult, ToDart(ZX_ERR_IO)); |
| zx_handle_t vmo = ZX_HANDLE_INVALID; |
| zx_status_t status = fdio_get_vmo_clone(fd.get(), &vmo); |
| if (status != ZX_OK) |
| return ConstructDartObject(kFromFileResult, ToDart(status)); |
| |
| return ConstructDartObject(kFromFileResult, ToDart(status), |
| ToDart(Handle::Create(vmo)), |
| ToDart(stat_struct.st_size)); |
| } |
| |
| Dart_Handle System::VmoGetSize(fxl::RefPtr<Handle> vmo) { |
| if (!vmo || !vmo->is_valid()) { |
| return ConstructDartObject(kGetSizeResult, ToDart(ZX_ERR_BAD_HANDLE)); |
| } |
| |
| uint64_t size; |
| zx_status_t status = zx_vmo_get_size(vmo->handle(), &size); |
| |
| return ConstructDartObject(kGetSizeResult, ToDart(status), ToDart(size)); |
| } |
| |
| zx_status_t System::VmoSetSize(fxl::RefPtr<Handle> vmo, uint64_t size) { |
| if (!vmo || !vmo->is_valid()) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| return zx_vmo_set_size(vmo->handle(), size); |
| } |
| |
| zx_status_t System::VmoWrite(fxl::RefPtr<Handle> vmo, |
| uint64_t offset, |
| const tonic::DartByteData& data) { |
| if (!vmo || !vmo->is_valid()) { |
| data.Release(); |
| return ZX_ERR_BAD_HANDLE; |
| } |
| |
| zx_status_t status = |
| zx_vmo_write(vmo->handle(), data.data(), offset, data.length_in_bytes()); |
| |
| data.Release(); |
| return status; |
| } |
| |
| Dart_Handle System::VmoRead(fxl::RefPtr<Handle> vmo, |
| uint64_t offset, |
| size_t size) { |
| if (!vmo || !vmo->is_valid()) { |
| return ConstructDartObject(kReadResult, ToDart(ZX_ERR_BAD_HANDLE)); |
| } |
| |
| // TODO: constrain size? |
| ByteDataScope bytes(size); |
| zx_status_t status = zx_vmo_read(vmo->handle(), bytes.data(), offset, size); |
| bytes.Release(); |
| if (status == ZX_OK) { |
| return ConstructDartObject(kReadResult, ToDart(status), bytes.dart_handle(), |
| ToDart(size)); |
| } |
| return ConstructDartObject(kReadResult, ToDart(status)); |
| } |
| |
| struct SizedRegion { |
| SizedRegion(void* r, size_t s) : region(r), size(s) {} |
| void* region; |
| size_t size; |
| }; |
| |
| void System::VmoMapFinalizer(void* isolate_callback_data, |
| Dart_WeakPersistentHandle handle, |
| void* peer) { |
| SizedRegion* r = reinterpret_cast<SizedRegion*>(peer); |
| zx_vmar_unmap(zx_vmar_root_self(), reinterpret_cast<uintptr_t>(r->region), |
| r->size); |
| delete r; |
| } |
| |
| Dart_Handle System::VmoMap(fxl::RefPtr<Handle> vmo) { |
| if (!vmo || !vmo->is_valid()) |
| return ConstructDartObject(kMapResult, ToDart(ZX_ERR_BAD_HANDLE)); |
| |
| uint64_t size; |
| zx_status_t status = zx_vmo_get_size(vmo->handle(), &size); |
| if (status != ZX_OK) |
| return ConstructDartObject(kMapResult, ToDart(status)); |
| |
| uintptr_t mapped_addr; |
| status = zx_vmar_map(zx_vmar_root_self(), ZX_VM_PERM_READ, 0, vmo->handle(), |
| 0, size, &mapped_addr); |
| if (status != ZX_OK) |
| return ConstructDartObject(kMapResult, ToDart(status)); |
| |
| void* data = reinterpret_cast<void*>(mapped_addr); |
| Dart_Handle object = Dart_NewExternalTypedData(Dart_TypedData_kUint8, data, |
| static_cast<intptr_t>(size)); |
| FXL_DCHECK(!tonic::LogIfError(object)); |
| |
| SizedRegion* r = new SizedRegion(data, size); |
| Dart_NewWeakPersistentHandle(object, reinterpret_cast<void*>(r), |
| static_cast<intptr_t>(size) + sizeof(*r), |
| System::VmoMapFinalizer); |
| |
| return ConstructDartObject(kMapResult, ToDart(ZX_OK), object); |
| } |
| |
| uint64_t System::ClockGet(uint32_t clock_id) { |
| return zx_clock_get(clock_id); |
| } |
| |
| // clang-format: off |
| |
| #define FOR_EACH_STATIC_BINDING(V) \ |
| V(System, ChannelCreate) \ |
| V(System, ChannelFromFile) \ |
| V(System, ChannelWrite) \ |
| V(System, ChannelQueryAndRead) \ |
| V(System, EventpairCreate) \ |
| V(System, Reboot) \ |
| V(System, SocketCreate) \ |
| V(System, SocketWrite) \ |
| V(System, SocketRead) \ |
| V(System, VmoCreate) \ |
| V(System, VmoFromFile) \ |
| V(System, VmoGetSize) \ |
| V(System, VmoSetSize) \ |
| V(System, VmoRead) \ |
| V(System, VmoWrite) \ |
| V(System, VmoMap) \ |
| V(System, ClockGet) |
| |
| // clang-format: on |
| |
| // Tonic is missing a comma. |
| #define DART_REGISTER_NATIVE_STATIC_(CLASS, METHOD) \ |
| DART_REGISTER_NATIVE_STATIC(CLASS, METHOD), |
| |
| FOR_EACH_STATIC_BINDING(DART_NATIVE_CALLBACK_STATIC) |
| |
| void System::RegisterNatives(tonic::DartLibraryNatives* natives) { |
| natives->Register({FOR_EACH_STATIC_BINDING(DART_REGISTER_NATIVE_STATIC_)}); |
| } |
| |
| } // namespace dart |
| } // namespace zircon |