blob: a6e922e20babac4533fbab5eadc363f9e5cd40d2 [file] [log] [blame]
// 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.
//go:build !build_with_native_toolchain
package component
import (
"context"
"fmt"
"syscall/zx"
"syscall/zx/fdio"
"syscall/zx/fidl"
"fidl/fuchsia/io"
)
// #include "zircon/process.h"
import "C"
const (
HandleDirectoryRequest HandleType = 0x3B
HandleAppServices HandleType = 0x43
HandleUser0 HandleType = 0xF0
HandleUser1 HandleType = 0xF1
HandleUser2 HandleType = 0xF2
)
type HandleType uint16
type HandleInfo struct {
Type HandleType
Arg uint16
}
func GetStartupHandle(info HandleInfo) zx.Handle {
id := uint32(info.Arg)<<16 | uint32(info.Type)
h := C.zx_take_startup_handle(C.uint32_t(id))
return zx.Handle(h)
}
type Connector struct {
serviceRoot *io.DirectoryWithCtxInterface
}
type OutDirectory mapDirectory
func (od OutDirectory) getMapDirectory(name string) mapDirectory {
if dir, ok := od[name]; ok {
if dir, ok := dir.(*DirectoryWrapper); ok {
if dir, ok := dir.Directory.(mapDirectory); ok {
return dir
}
panic(fmt.Sprintf("unexpected %s type %T", name, dir))
}
panic(fmt.Sprintf("unexpected %s type %T", name, dir))
}
dir := make(mapDirectory)
od[name] = &DirectoryWrapper{
Directory: dir,
}
return dir
}
func (od OutDirectory) AddDebug(name string, n Node) {
od.getMapDirectory("debug")[name] = n
}
func (od OutDirectory) AddDiagnostics(name string, n Node) {
od.getMapDirectory("diagnostics")[name] = n
}
// AddService registers a named handler in the outgoing service directory.
//
// The handler function is called serially with each incoming request matching
// the name. It must not block, and is expected to handle incoming calls on the
// request.
func (od OutDirectory) AddService(name string, addFn func(context.Context, zx.Channel) error) {
od.getMapDirectory("svc")[name] = &Service{AddFn: addFn}
}
type Context struct {
connector Connector
// OutgoingService is the directory served on the startup directory request.
//
// OutgoingService is cleared when BindStartupHandle is called to prevent further mutation which
// is not supported by this implementation, though it is permitted by fuchsia.io.Directory.
OutgoingService OutDirectory
}
// NewContextFromStartupInfo connects to the service root directory and registers
// debug services.
func NewContextFromStartupInfo() *Context {
r, p, err := io.NewDirectoryWithCtxInterfaceRequest()
if err != nil {
panic(err)
}
// NB: The leading slash is required, for some reason:
//
// https://cs.opensource.google/fuchsia/fuchsia/+/main:third_party/go/src/syscall/zx/fdio/namespace.go;l=45-48;drc=5ca02efa3b88b042c50c4af0292f3b93436f489a
//
// NB: The trailing slash is required; it is stripped unconditionally:
//
// https://cs.opensource.google/fuchsia/fuchsia/+/main:third_party/go/src/syscall/zx/fdio/namespace.go;l=56;drc=5ca02efa3b88b042c50c4af0292f3b93436f489a
//
// NB: The trailing period is required because empty paths are invalid:
//
// https://cs.opensource.google/fuchsia/fuchsia/+/main:sdk/fidl/fuchsia.io/directory.fidl;l=197-201;drc=469b24f881dbc73b8adbfdedfd2867c15802d7a7
if err := fdio.ServiceConnect("/svc/.", zx.Handle(r.Channel)); err != nil {
panic(err)
}
c := &Context{
connector: Connector{
serviceRoot: p,
},
OutgoingService: make(OutDirectory),
}
c.OutgoingService.AddDebug("pprof", &DirectoryWrapper{Directory: &pprofDirectory{}})
c.OutgoingService.AddDebug("goroutines", &FileWrapper{File: &stackTraceFile{}})
return c
}
// BindStartupHandle takes the startup handle if it's not already taken and
// serves the out directory with all services that have been registered on
// (*Context).OutgoingService.
//
// New services must not be added after BindStartupHandle is called.
//
// BindStartupHandle blocks until the context is canceled.
func (c *Context) BindStartupHandle(ctx fidl.Context) {
directory := mapDirectory(c.OutgoingService)
// Prevent further mutation.
c.OutgoingService = nil
if directoryRequest := GetStartupHandle(HandleInfo{
Type: HandleDirectoryRequest,
Arg: 0,
}); directoryRequest.IsValid() {
if err := (&DirectoryWrapper{
Directory: directory,
}).addConnection(ctx, 0, 0, io.NodeWithCtxInterfaceRequest{
Channel: zx.Channel(directoryRequest),
}); err != nil {
panic(err)
}
}
<-ctx.Done()
}
func (c *Context) Connector() *Connector {
return &c.connector
}
func (c *Context) ConnectToEnvService(r fidl.ServiceRequest) {
if err := c.ConnectToProtocolAtPath(r.Name(), r); err != nil {
panic(err)
}
}
func (c *Context) ConnectToProtocolAtPath(path string, r fidl.ServiceRequest) error {
return c.Connector().ConnectToProtocolAtPath(path, r)
}
func (c *Connector) ConnectToProtocolAtPath(path string, r fidl.ServiceRequest) error {
return c.serviceRoot.Open(
context.Background(),
io.OpenFlags(0),
0,
path,
io.NodeWithCtxInterfaceRequest{
Channel: r.ToChannel(),
},
)
}