| // 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. |
| |
| // `go mod` ignores file names for the purpose of resolving |
| // dependencies, and zxwait doesn't build on not-Fuchsia. |
| //go:build fuchsia |
| |
| package os |
| |
| // #cgo fuchsia CFLAGS: -I${SRCDIR}/../../../../../zircon/system/ulib/fdio/include |
| // #cgo fuchsia LDFLAGS: -lfdio |
| // |
| // #include <lib/fdio/fd.h> |
| // #include <lib/fdio/spawn.h> |
| // #include <stdlib.h> |
| // #include <unistd.h> |
| // #include <zircon/syscalls/object.h> |
| // #include <zircon/types.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 ( |
| "context" |
| "errors" |
| "syscall" |
| "syscall/zx" |
| "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 |
| } |
| |
| mCopy, err := m.Clone() |
| if err != nil { |
| return -1, err |
| } |
| handles := mCopy.Handles() |
| |
| var fd C.int |
| status := zx.Status(C.fdio_fd_create(C.zx_handle_t(handles[0]), &fd)) |
| if status != zx.ErrOk { |
| return -1, errors.New("fdio_fd_create 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 { |
| const prefix = "PWD=" |
| // strings.HasPrefix |
| if len(s) >= len(prefix) && s[:len(prefix)] == prefix { |
| 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, |
| // TODO(mdempsky): Flags. |
| C.FDIO_SPAWN_CLONE_JOB|C.FDIO_SPAWN_DEFAULT_LDSVC|C.FDIO_SPAWN_CLONE_NAMESPACE|C.FDIO_SPAWN_CLONE_UTC_CLOCK, |
| nameC, |
| &argvC[0], |
| &envC[0], |
| C.size_t(len(actions)), |
| actions0, |
| &h, |
| &errmsg[0], |
| )) |
| if status != zx.ErrOk { |
| return 0, &zx.Error{ |
| Status: status, |
| Text: "fdio_spawn_etc: " + 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 &zx.Error{ |
| Status: status, |
| Text: "kill error", |
| } |
| } |
| return nil |
| } |
| |
| func (p *Process) wait() (*ProcessState, error) { |
| procHandle := zx.Handle(p.handle) |
| if _, err := zxwait.WaitContext(context.Background(), procHandle, zx.SignalTaskTerminated); 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, &zx.Error{ |
| Status: status, |
| Text: "error retrieving process info", |
| } |
| } |
| return &ProcessState{retCode: int(procInfo.return_code)}, p.Release() |
| } |