| // Copyright 2017 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // +build fuchsia |
| |
| package os |
| |
| // #cgo fuchsia CFLAGS: -I${SRCDIR}/../../../../../zircon/system/ulib/fdio/include |
| // #cgo fuchsia LDFLAGS: -lfdio |
| // |
| // #include <zircon/syscalls/object.h> |
| // #include <zircon/types.h> |
| // #include <lib/fdio/spawn.h> |
| // #include <lib/fdio/util.h> |
| // #include <stdlib.h> |
| // |
| // static int fsa_get_local_fd(fdio_spawn_action_t *fsa) { return fsa->fd.local_fd; } |
| // static int fsa_get_target_fd(fdio_spawn_action_t *fsa) { return fsa->fd.target_fd; } |
| // |
| // static void fsa_set_local_fd(fdio_spawn_action_t *fsa, int fd) { fsa->fd.local_fd = fd; } |
| // static void fsa_set_target_fd(fdio_spawn_action_t *fsa, int fd) { fsa->fd.target_fd = fd; } |
| import "C" |
| |
| import ( |
| "errors" |
| "strings" |
| "syscall" |
| "syscall/zx" |
| "syscall/zx/fdio" |
| "syscall/zx/zxwait" |
| "unsafe" |
| ) |
| |
| func makeCStringArray(s []string) []*C.char { |
| ret := make([]*C.char, len(s)+1) |
| for i, s := range s { |
| ret[i] = (*C.char)(C.CString(s)) |
| } |
| ret[len(ret)-1] = nil |
| return ret |
| } |
| |
| func freeCStringArray(a []*C.char) { |
| for i := range a { |
| if a[i] != nil { |
| C.free(unsafe.Pointer(a[i])) |
| } |
| } |
| } |
| |
| // asLibfdioFD returns a File as a libfdio file descriptor. |
| func asLibfdioFD(f *File) (int, error) { |
| m := syscall.FDIOForFD(int(f.Fd())) |
| if m == nil { |
| return -1, ErrInvalid |
| } |
| |
| var fdioType C.uint32_t |
| switch m.(type) { |
| case *fdio.Object, *fdio.Node, *fdio.File, *fdio.Directory: |
| fdioType = fdio.HandleTypeRemote |
| case *fdio.Pipe: |
| fdioType = fdio.HandleTypePipe |
| case *fdio.Logger: |
| fdioType = fdio.HandleTypeLogger |
| default: |
| return -1, ErrInvalid |
| } |
| |
| mCopy, err := m.Clone() |
| if err != nil { |
| return -1, err |
| } |
| handles := mCopy.Handles() |
| |
| var handlesC [fdio.MaxUnderlyingHandles]C.zx_handle_t |
| var typesC [fdio.MaxUnderlyingHandles]C.uint32_t |
| for i, h := range handles { |
| handlesC[i] = C.zx_handle_t(h) |
| typesC[i] = C.uint32_t(fdioType) |
| } |
| |
| var fd C.int |
| status := zx.Status(C.fdio_create_fd(&handlesC[0], &typesC[0], C.size_t(len(handles)), &fd)) |
| if status != zx.ErrOk { |
| mCopy.Close() |
| return -1, errors.New("fdio_create_fd failed") |
| } |
| return int(fd), nil |
| } |
| |
| func fdioSpawnActions(attr *ProcAttr) (actions []C.fdio_spawn_action_t, err error) { |
| defer func() { |
| if err != nil { |
| for _, action := range actions { |
| C.close(C.fsa_get_local_fd(&action)) |
| } |
| } |
| }() |
| |
| for i, f := range attr.Files { |
| if f == nil { |
| continue |
| } |
| fd, err := asLibfdioFD(f) |
| if err != nil { |
| return nil, err |
| } |
| action := C.fdio_spawn_action_t{action: C.FDIO_SPAWN_ACTION_TRANSFER_FD} |
| C.fsa_set_local_fd(&action, C.int(fd)) |
| C.fsa_set_target_fd(&action, C.int(i)) |
| actions = append(actions, action) |
| } |
| |
| return actions, nil |
| } |
| |
| func fdioStartProcess(name string, argv []string, attr *ProcAttr) (zx.Handle, error) { |
| env := attr.Env |
| if env == nil { |
| env = Environ() |
| } |
| if attr.Dir != "" { |
| found := false |
| for i, s := range env { |
| if strings.HasPrefix(s, "PWD=") { |
| found = true |
| env[i] = "PWD=" + attr.Dir |
| break |
| } |
| } |
| if !found { |
| env = append(env, "PWD="+attr.Dir) |
| } |
| } |
| |
| nameC := C.CString(name) |
| defer C.free(unsafe.Pointer(nameC)) |
| argvC := makeCStringArray(argv) |
| defer freeCStringArray(argvC) |
| envC := makeCStringArray(env) |
| defer freeCStringArray(envC) |
| |
| actions, err := fdioSpawnActions(attr) |
| if err != nil { |
| return 0, err |
| } |
| |
| var actions0 *C.fdio_spawn_action_t |
| if len(actions) > 0 { |
| actions0 = &actions[0] |
| } |
| |
| var h C.zx_handle_t |
| var errmsg [C.FDIO_SPAWN_ERR_MSG_MAX_LENGTH]C.char |
| status := zx.Status(C.fdio_spawn_etc( |
| C.ZX_HANDLE_INVALID, |
| C.FDIO_SPAWN_CLONE_JOB | C.FDIO_SPAWN_CLONE_LDSVC | C.FDIO_SPAWN_CLONE_NAMESPACE, // TODO(mdempsky): Flags. |
| nameC, |
| &argvC[0], |
| &envC[0], |
| C.size_t(len(actions)), |
| actions0, |
| &h, |
| &errmsg[0], |
| )) |
| if status != zx.ErrOk { |
| return 0, errors.New("fdio_spawn_etc: " + itoa(int(status)) + ": " + charsAsString(errmsg[:])) |
| } |
| return zx.Handle(h), nil |
| } |
| |
| func charsAsString(s []C.char) string { |
| var x []byte |
| for _, c := range s { |
| if c == 0 { |
| break |
| } |
| x = append(x, byte(c)) |
| } |
| return string(x) |
| } |
| |
| func (p *Process) kill() error { |
| procHandle := zx.Handle(p.handle) |
| status := zx.Sys_task_kill(procHandle) |
| if status != zx.ErrOk { |
| return errors.New("kill error: " + itoa(int(status))) |
| } |
| return nil |
| } |
| |
| func (p *Process) wait() (ps *ProcessState, err error) { |
| procHandle := zx.Handle(p.handle) |
| _, err = zxwait.Wait(procHandle, zx.SignalTaskTerminated, zx.TimensecInfinite) |
| if err != nil { |
| return nil, err |
| } |
| procInfo := C.zx_info_process_t{} |
| status := zx.Sys_object_get_info(procHandle, zx.ObjectInfoProcess, unsafe.Pointer(&procInfo), C.sizeof_zx_info_process_t, nil, nil) |
| if status != zx.ErrOk { |
| return nil, errors.New("error retreiving process info: " + itoa(int(status))) |
| } |
| return &ProcessState{int(procInfo.return_code)}, nil |
| } |