| // Copyright 2019 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 "fdio.h" |
| |
| #include <lib/fdio/directory.h> |
| #include <lib/fdio/namespace.h> |
| #include <zircon/syscalls.h> |
| #include <zircon/types.h> |
| |
| #include <iterator> |
| |
| #include "src/developer/shell/josh/lib/qjs_util.h" |
| #include "src/developer/shell/josh/lib/zx.h" |
| #include "third_party/quickjs/quickjs.h" |
| |
| namespace shell::fdio { |
| |
| namespace { |
| |
| // Wrapper around fdio_service_connect. |
| // argv[0] is a (string) path to the service. |
| // Returns a handle object that points to the client endpoint to this service. |
| JSValue ServiceConnect(JSContext *ctx, JSValueConst /*this_val*/, int argc, JSValueConst *argv) { |
| if (argc != 1) { |
| return JS_ThrowSyntaxError(ctx, |
| "Wrong number of arguments to fdio.serviceConnect(), " |
| "was %d, expected 1", |
| argc); |
| } |
| zx_handle_t out0, out1; |
| zx_status_t status; |
| |
| status = zx_channel_create(0, &out0, &out1); |
| if (status != ZX_OK) { |
| return zx::ZxStatusToError(ctx, status); |
| } |
| |
| CStringHolder path(ctx, argv[0]); |
| if (!path.get()) { |
| return JS_EXCEPTION; |
| } |
| |
| status = fdio_service_connect(path.get(), out1); |
| |
| if (status != ZX_OK) { |
| zx_handle_close(out0); |
| zx_handle_close(out1); |
| return zx::ZxStatusToError(ctx, status); |
| } |
| return zx::HandleCreate(ctx, out0, ZX_OBJ_TYPE_CHANNEL); |
| } |
| |
| // Makes the root handles (representing elements of the namespace) available to JS callers. |
| |
| JSClassDef flat_ns_class_ = { |
| "FlatNs", |
| .finalizer = nullptr, |
| }; |
| |
| JSClassID flat_ns_class_id_; |
| |
| // Returns an object that represents the root namespace. |
| JSValue NsExportRoot(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { |
| fdio_flat_namespace_t *root_ns = nullptr; |
| zx_status_t status = fdio_ns_export_root(&root_ns); |
| if (status != ZX_OK) { |
| return zx::ZxStatusToError(ctx, status); |
| } |
| JSValue flat_ns_obj = JS_NewObjectClass(ctx, flat_ns_class_id_); |
| JS_SetOpaque(flat_ns_obj, reinterpret_cast<void *>(root_ns)); |
| return flat_ns_obj; |
| } |
| |
| // Closes the elements in the root namespace associated with this object. |
| JSValue NsClose(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { |
| fdio_flat_namespace_t *ns = |
| reinterpret_cast<fdio_flat_namespace_t *>(JS_GetOpaque(this_val, flat_ns_class_id_)); |
| fdio_ns_free_flat_ns(ns); |
| return JS_NewInt32(ctx, 0); |
| } |
| |
| // Gets the number of handles in the root namespace associated with this object. |
| JSValue NsGetCount(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { |
| fdio_flat_namespace_t *ns = |
| reinterpret_cast<fdio_flat_namespace_t *>(JS_GetOpaque(this_val, flat_ns_class_id_)); |
| return JS_NewInt32(ctx, ns->count); |
| } |
| |
| // Gets a list of Handle objects that refer to the root namespace. They have |
| // a handle property and a property representing the type, which is an int |
| // defined by the PA_HND macro in processargs.h. |
| JSValue NsGetElements(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { |
| fdio_flat_namespace_t *ns = |
| reinterpret_cast<fdio_flat_namespace_t *>(JS_GetOpaque(this_val, flat_ns_class_id_)); |
| if (ns == nullptr) { |
| return JS_EXCEPTION; |
| } |
| JSValue dirents = JS_NewObject(ctx); |
| if (JS_IsException(dirents)) { |
| return dirents; |
| } |
| for (size_t i = 0; i < ns->count; i++) { |
| JSValue val = JS_NewObject(ctx); |
| if (JS_IsException(val)) { |
| return val; |
| } |
| if (zx_object_get_info(ns->handle[i], ZX_INFO_HANDLE_VALID, nullptr, 0, nullptr, nullptr) == |
| ZX_ERR_BAD_HANDLE) { |
| continue; |
| } |
| if (JS_SetPropertyStr(ctx, val, "handle", |
| zx::HandleCreate(ctx, ns->handle[i], ZX_OBJ_TYPE_NONE)) < 0) { |
| return JS_EXCEPTION; |
| } |
| if (JS_SetPropertyStr(ctx, val, "type", JS_NewInt32(ctx, ns->type[i])) < 0) { |
| return JS_EXCEPTION; |
| } |
| if (JS_SetPropertyStr(ctx, dirents, ns->path[i], val) < 0) { |
| return JS_EXCEPTION; |
| } |
| } |
| return dirents; |
| } |
| |
| const JSCFunctionListEntry flat_ns_proto_funcs_[] = { |
| JS_CFUNC_DEF("getCount", 0, NsGetCount), |
| JS_CFUNC_DEF("getElements", 0, NsGetElements), |
| JS_CFUNC_DEF("close", 0, NsClose), |
| }; |
| |
| const JSCFunctionListEntry funcs_[] = { |
| JS_CFUNC_DEF("serviceConnect", 1, ServiceConnect), |
| JS_CFUNC_DEF("nsExportRoot", 0, NsExportRoot), |
| }; |
| |
| int FdioRunOnInit(JSContext *ctx, JSModuleDef *m) { |
| JS_NewClassID(&flat_ns_class_id_); |
| JS_NewClass(JS_GetRuntime(ctx), flat_ns_class_id_, &flat_ns_class_); |
| JSValue proto = JS_NewObject(ctx); |
| JS_SetPropertyFunctionList(ctx, proto, flat_ns_proto_funcs_, std::size(flat_ns_proto_funcs_)); |
| JS_SetClassProto(ctx, flat_ns_class_id_, proto); |
| |
| return JS_SetModuleExportList(ctx, m, funcs_, std::size(funcs_)); |
| } |
| |
| } // namespace |
| |
| // Returns a module that supports FDIO functionality. |
| JSModuleDef *FdioModuleInit(JSContext *ctx, const char *module_name) { |
| JSModuleDef *m = JS_NewCModule(ctx, module_name, FdioRunOnInit); |
| if (!m) { |
| return nullptr; |
| } |
| JS_AddModuleExportList(ctx, m, funcs_, std::size(funcs_)); |
| return m; |
| } |
| } // namespace shell::fdio |