blob: 7c8c40c3c9d1563d02a09cd57d6713f4e69c066d [file] [log] [blame]
// 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 <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 (
"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,
C.FDIO_SPAWN_CLONE_JOB|C.FDIO_SPAWN_DEFAULT_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 retrieving process info: " + itoa(int(status)))
}
defer p.Release()
return &ProcessState{int(procInfo.return_code)}, nil
}